7

For the following program:

#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
    for (float a = 1.0; a < 10; a++)
        cout << std::setprecision(30) << 1.0/a << endl;
    return 0;
}

I recieve the following output:

1
0.5
0.333333333333333314829616256247
0.25
0.200000000000000011102230246252
0.166666666666666657414808128124
0.142857142857142849212692681249
0.125
0.111111111111111104943205418749

Which is definitely not right right for the lower place digits, particularly with respect to 1/3,1/5,1/7, and 1/9. things just start going wrong around 10^-16 I would expect to see out put more resembling:

1
0.5
0.333333333333333333333333333333
0.25
0.2
0.166666666666666666666666666666
0.142857142857142857142857142857
0.125
0.111111111111111111111111111111

Is this an inherit flaw in the float class? Is there a way to overcome this and have proper division? Is there a special datatype for doing precise decimal operations? Am I just doing something stupid or wrong in my example?

elder south
  • 463
  • 2
  • 4
  • 14
  • Guess how much precision they max at. – chris May 15 '13 at 20:44
  • What do you expect floating point arithmetic to do? – danielschemmel May 15 '13 at 20:46
  • well i posted in my question that they max at 10^-16, and i expected to get the kind of output that i listed at the end, although based on these questions I'm guessing that you guys are going to point out that i'm overloading the datatype and i'm going to feel stupid for asking this question. – elder south May 15 '13 at 20:48
  • 2
    Floating point acts like scientific notation, with ~6.92 decimal digits of accuracy. `double` gets you about ~15.65 decimal digits of accuracy. If you want _perfect_ precision, you'll need a special `rational` class, which are large and very slow. The fractional bits of accuracy are because it's binary internally, `float` is exactly 23 binary digits accurate, and 53 binary digits for double. – Mooing Duck May 15 '13 at 20:48
  • C99 also has a `long double` type that _may or may not_ be more accurate, depending on your compiler, with accuracy anywhere from 53 binary digits (~15.65 decimal) to 113 binary digits (~34.02 decimal). – Mooing Duck May 15 '13 at 20:56
  • 1
    Try printing out `std::numeric_limits::digits10` for `T` = `float`, `double` and `long double`, to get at least a reasonable approximation of the number of digits of precision you can expect from your implementation's `float`, `double` and `long double` respectively. – Jerry Coffin May 15 '13 at 21:04

3 Answers3

6

There are a lot of numbers that computers cannot represent, even if you use float or double-precision float. 1/3, or .3 repeating, is one of those numbers. So it just does the best it can, which is the result you get.

See http://floating-point-gui.de/, or google float precision, there's a ton of info out there (including many SO questions) on this subject.

To answer your questions -- yes, this is an inherent limitation in both the float class and the double class. Some mathematical programs (MathCAD, probably Mathematica) can do "symbolic" math, which allows calculation of the "correct" answers. In many cases, the round-off error can be managed, even over really complex computations, such that the top 6-8 decimal places are correct. However, the opposite is true as well -- naive computations can be constructed that return wildly incorrect answers.

For small problems like division of whole numbers, you'll get a decent number of decimal place accuracy (maybe 4-6 places). If you use double precision floats, that will go up to maybe 8. If you need more... well, I'd start questioning why you want that many decimal places.

Colin Nichols
  • 714
  • 4
  • 12
  • thanks for the info, and as far as why i need so many decimal places is because i'm working on a project euler project where i need to identify long series of repeating decimals. which is why i have never worried about precision until today. – elder south May 15 '13 at 20:57
  • @eldersouth In that case, that is a problem only concerned with integers and integer arithmetic. Using floating point numbers is wrong for that (real rationals with arbitrary precision integers could be an option, but plain machine `int` arithmetic is the easiest and best way). – Daniel Fischer May 15 '13 at 20:59
  • 2
    I'm pretty sure that identifying repeating sequences can be done by using the "long divide" method, just like you would do when dividing 12341 by 73 by hand (or dividing 10 by 7, and so on) [Edit: and this can be done in integer mathematics] – Mats Petersson May 15 '13 at 21:00
  • Agree with Mats -- doing it out "by hand" is probably the best strategy for your particular problem! – Colin Nichols May 15 '13 at 21:02
  • +1 for recommending http://floating-point-gui.de instead of that other long document. – milleniumbug May 15 '13 at 21:51
6

First of all, since your code does 1.0/a, it gives you double (1.0 is a double value, 1.0f is float) as the rules of C++ (and C) always extends a smaller type to the larger one if the operands of an operation is different size (so, int + char makes the char into an int before adding the values, long + int will make the int long, etc, etc).

Second floating point values have a set number of bits for the "number". In float, that is 23 bits (+ 1 'hidden' bit), and in double it's 52 bits (+1). Yet get approximately 3 digits per bit (exactly: log2(10), if we use decimal number representation), so a 23 bit number gives approximately 7-8 digits, a 53 bit number approximately 16-17 digits. The remainder is just "noise" caused by the last few bits of the number not evening out when converting to a decimal number.

To have infinite precision, we would have to either store the value as a fraction, or have an infinite number of bits. And of course, we could have some other finite precision, such as 100 bits, but I'm sure you'd complain about that too, because it would just have another 15 or so digits before it "goes wrong".

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • @MooingDuck: Actually I took them from my memory, which is obviously also messed up. Now fixed. – Mats Petersson May 15 '13 at 21:01
  • The remainder isn't _entirely_ noise. Float should have 6 accurate digits, and then the next digit is usually accurate, with an 8% chance of being off by one. (unless of course it rolls over, causing preceeding digits to be rounded up). – Mooing Duck May 15 '13 at 21:16
  • @MooingDuck: No, I meant that when you take the last decimal digit that is "accurate", the next will only provide a partial digit, and the remainder of the number is noise caused by trying to continually multiply/divide that number down into decimals [and possibly accumulated errors from previous partial calculations] – Mats Petersson May 15 '13 at 21:19
2

Floats only have so much precision (23 bits worth to be precise). If you REALLY want to see "0.333333333333333333333333333333" output, you could create a custom "Fraction" class which stores the numerator and denominator separately. Then you could calculate the digit at any given point with complete accuracy.

Taylor Brandstetter
  • 3,523
  • 15
  • 24