-1

If double precision does not guarantee more than 16 significant decimal digits, how is such an output generated by this standard C++ program? Also small value change operations done on "ans" such as ++ans don't alter the screen output. Is the answer "computed" just before printing the result instead of getting stored in IEEE754 double-precision format into "ans" rightaway?

Restating the question - Why such garbage digits that follow first 16 digits produce correct results for powers of 2?

#include <cstdio>
#include <cmath>
using namespace std;

int main() {
    double ans = pow(2.0, 700.0);
    printf("%.0f\n", ans);
    return 0;
}

5260135901548373507240989882880128665550339802823173859498280903068732154297080822113666536277588451226982968856178217713019432250183803863127814770651880849955223671128444598191663757884322717271293251735781376

SOLVED: Also found an explanation here.

Thanks for all the responses!

  • only the first 17 digits are correct. The correct result is `98654723617344326803756012219710387900068624000476928022981291632472095676838147798449116716829231674619518938741315839399284558304881843191019178346548130225289323690210732453184437107459468590269272102572684062394518819394753059628479060760937563207` – mch Aug 22 '14 at 13:30
  • 1
    @mch power of two can't end with 7. In fact the correct result is in the post. – Anton Savin Aug 22 '14 at 13:36
  • The question changed from 7^297 to 2^700, so my number is only correct for the first version of the question – mch Aug 22 '14 at 13:39
  • Yes, this trick works only with powers of 2 and only with integer powers up to 1023. – Anton Savin Aug 22 '14 at 14:09

4 Answers4

3

You still only have about 16 significant decimal digits; this means that only the first 16 or so digits of the output are correct; and all of the remaining digits are likely wildly incorrect, and are not actually stored as part of the double-precision number. This is why adding 1 to the number doesn't do anything -- it's a change to one of the insignificant digits that isn't actually recorded.

cdhowie
  • 133,716
  • 21
  • 261
  • 264
  • But the number in the original question is really a 2**700. Tried in Python: >>> 2**700 5260135901548373507240989882880128665550339802823173859498280903068732154297080822113666536277588451226982968856178217713019432250183803863127814770651880849955223671128444598191663757884322717271293251735781376L – Anton Savin Aug 22 '14 at 13:51
  • 1
    None of the decimal digits is stored as part of a floating point number. – James Kanze Aug 22 '14 at 14:26
  • @AntonSavin In that case, the insignificant digits are only correct because it is an exact power of two. The insignificant digits are not **guaranteed** to be correct at all, which is why they are called insignificant. – cdhowie Aug 22 '14 at 17:07
3

In fact, 2**700 can be precisely represented in IEEE double because it has 11 bits for exponent part, and so it can be precisely printed, and some compilers generate such code. But 2**700 + 1 of course cannot be represented precisely and it rounds to 2**700.

And of course this trick works only with powers of 2 and only with integer powers up to 1023 (exponent is a signed number).

Anton Savin
  • 38,277
  • 8
  • 49
  • 82
1

In IEEE double representations, the value is encoded using a bit to track the + or - sign, a mantissa that encodes a number between 1 and 1 + 2^52-1 / 2^52 in increments of 1 / 2^52, and an 11 bit exponent encoding a multiplier between 2^-1031 and 2^1032. To quote Wikipedia:

Between 2^52=4,503,599,627,370,496 and 2^53=9,007,199,254,740,992 the representable numbers are exactly the integers. For the next range, from 2^53 to 2^54, everything is multiplied by 2, so the representable numbers are the even ones, etc..

So, by the time you're trying to store 2^700 into a double, the exact value of the double will be incrementing by 2^648 every time the mantissa increments to the next representable value. To "store" any value that doesn't happen to be one of these exact values, you have to compromise and round/truncate to the nearest exactly representable value. So, when you ask to print the number with no decimal places but all the digits, the compiler may be telling you the exact value being representing, even though that's different from the number you asked it to store... for 7^297 what's stored was after rounding. For 2^700 though, no rounding is required... a mantissa of 1 and exponent of 700 encodes it exactly.

My point is that the extra digits aren't necessarily arbitrary garbage - a good printf implementation should be displaying what's genuinely part of the value of the double, and potentially meaningful if the number wasn't rounded when stored.

Tony Delroy
  • 94,554
  • 11
  • 158
  • 229
0

IEEE754 gives from 15–17 significant decimal digits according to Wikipedia. I've compiled this on my Windows machine with gcc 4.8.1 and it produces 98654723617344328000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000

For me, with the flags -std=c++11 -O2 -Wall -pedantic, g++ produced

fld qword [ 0x405070 ]
fstp qword [ esp  + 0x4 ]
call 0x403648  <printf>

Which admitedly is a bit out of my comfort zone, but it looks like it's just loading the double and throwing it on the stack. Seems like @AntonSavin is right, the double is printed to its best ability after rounding, which happens to be correct, and is why ans+1 doesn't change.

SteveC
  • 357
  • 1
  • 4
  • 10
  • Some gcc versions apparently do it: http://coliru.stacked-crooked.com/a/dfb3b82aae862723 – Anton Savin Aug 22 '14 at 13:39
  • @AntonSavin Yes, it seems the key compiler flag was `-std=c++11`. I also noticed with the flags in your example, the compiler pre-computed the value but it still uses `fld qword [ 0x405070 ] ; fstp qword [ esp + 0x4 ] call 0x403648 ` Which admitedly is a bit out of my comfort zone, but it looks like it's just loading the double. – SteveC Aug 22 '14 at 14:17