pow(), precision, and large powers of 2.

D

David Mathog

I just observed that

double var;
double N; /* an integer */
var=pow(2.0,N)
fprintf(stdout,"%.*lf\n",0,var);

Returns what appears to be the right value for everything up to
N=1023. At 1024 it returns inf.

Example:
2^256
115792089237316195423570985008687907853269984665640564039457584007913129639936

(The highest value I could find in a quick web search, to verify that
the long string was correct.)

Verified that the double wasn't somehow miraculously carrying that
much precision (linking in an arbitrary precision library or something
along those lines), by repeating the calculation with

var=pow(2.0,N)-100

As expected, it returned the same value for large N as did the
original. So no miraculous precision in general.

I assume that fprintf is somehow deriving all of these (correct)
digits from the exponent when it goes to print the double. Other than
the integer powers of 2, are there any other "extended precision"
values that can be found this way? Is this standard behavior, or just
something the gcc compiler does?

Thanks,

David Mathog
 
S

Seebs

As expected, it returned the same value for large N as did the
original. So no miraculous precision in general.
Right.

I assume that fprintf is somehow deriving all of these (correct)
digits from the exponent when it goes to print the double. Other than
the integer powers of 2, are there any other "extended precision"
values that can be found this way? Is this standard behavior, or just
something the gcc compiler does?

Nearly all floating point systems work by having a fractional part, which
may be any value from 0 to 1 (inclusive), plus a base-2 exponent, which
can be any value in some range. And then, yes, when it comes time to
display the value, it's just reconstituted.

For a lot of C implementations, you can see this by messing around with
values on the rough order of 2^24 in a plain float. e.g., 2^26 + 1 will not
differ from 2^26.

I'm not sure what you mean by "standard" behavior. It's normal for floating
point numbers to behave that way at the fringes of their range, yes.

-s
 
K

Keith Thompson

David Mathog said:
I just observed that

double var;
double N; /* an integer */
var=pow(2.0,N)
fprintf(stdout,"%.*lf\n",0,var);

Returns what appears to be the right value for everything up to
N=1023. At 1024 it returns inf.

Example:
2^256
115792089237316195423570985008687907853269984665640564039457584007913129639936

(The highest value I could find in a quick web search, to verify that
the long string was correct.)

Verified that the double wasn't somehow miraculously carrying that
much precision (linking in an arbitrary precision library or something
along those lines), by repeating the calculation with

var=pow(2.0,N)-100

As expected, it returned the same value for large N as did the
original. So no miraculous precision in general.

I assume that fprintf is somehow deriving all of these (correct)
digits from the exponent when it goes to print the double. Other than
the integer powers of 2, are there any other "extended precision"
values that can be found this way? Is this standard behavior, or just
something the gcc compiler does?

You're just seeing the result of the way floating-point is (usually)
implemented.

Floating-point numbers are usually stored in binary, with the bits of
storage divided as 1 bit for the sign, some number of bits for the
exponent (11 in the case of IEEE double), and the rest for the
"significand" (sometimes called the "mantissa"). There are some
additional details which we can ignore for now, but basically any
floating-point number is stored as sign * 2**exponent * significand. If
there are 11 bits for the exponent, it can be anywhere in the range
-1023 .. +1023.

Since the value isn't stored in decimal, the value
115792089237316195423570985008687907853269984665640564039457584007913129639936
isn't as precise as it looks. It's really stored as something like
sign=1, exponent=256 (100000000 binary), significand=1. Or perhaps
sign=1, exponent=257, significand=0.111111111... binary.

Google Goldberg's classic paper "What Every Computer Scientist
Should Know About Floating-Point Arithmetic". For a less
detailed explanation, see section 14 of the comp.lang.c FAQ,
<http://c-faq.com/>.

(I generally post the URL of the index of the FAQ, rather than the
specific section or question I'm referring to, to encourage people
to browse rather than just read one piece. It's easy enough to
find a specific section.)
 
N

Nobody

I assume that fprintf is somehow deriving all of these (correct)
digits from the exponent when it goes to print the double. Other than
the integer powers of 2, are there any other "extended precision"
values that can be found this way?

While most decimal fractions cannot be represented exactly in
floating-point, the converse isn't true; any floating-point number can be
represented exactly as a decimal fraction. There's no reason for the
*printf functions to produce anything other than the correct result.
Is this standard behavior, or just something the gcc compiler does?

printf() etc are part of the C library, not the compiler.
 
B

BartC

David Mathog said:
I just observed that

double var;
double N; /* an integer */
var=pow(2.0,N)
fprintf(stdout,"%.*lf\n",0,var);

Returns what appears to be the right value for everything up to
N=1023. At 1024 it returns inf.

Example:
2^256
115792089237316195423570985008687907853269984665640564039457584007913129639936

(The highest value I could find in a quick web search, to verify that
the long string was correct.)

Verified that the double wasn't somehow miraculously carrying that
much precision (linking in an arbitrary precision library or something
along those lines), by repeating the calculation with

var=pow(2.0,N)-100

As expected, it returned the same value for large N as did the
original. So no miraculous precision in general.

I assume that fprintf is somehow deriving all of these (correct)
digits from the exponent when it goes to print the double. Other than
the integer powers of 2, are there any other "extended precision"
values that can be found this way? Is this standard behavior, or just
something the gcc compiler does?

Try printing 2^256 in binary (which will be something like:

10000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000)

Then you can see there is very little precision involved.

Doubles have a limited maximum exponent, typically 11 bits (a range of about
2048, including negative exponents), so is not surprising it goes wrong at
+1024.
 
D

David Mathog

Try printing 2^256 in binary (which will be something like:

10000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000)

Then you can see there is very little precision involved.

I understand that. Let me rephrase. What is the point of printing
digits with something
other than 0 in them beyond the precision of the numeric
representation in the Mantissa?
They happen to be right for exact powers of 2^128, but certainly are
not in general. For instance:
2.0001^128 is

342467103150178572155544986495756533760

but using bc for the same calculation (arbitrary precision)

342467103150173953551119052597257504919

so would it not be just as appropriate, and less misleading, for
printf to instead emit:

34246710315017000000000000000000000000000

?
The long row of 0's looks like a precision limit, whereas in the cases
above there is nothing
obvious to indicate that limit. (Yes, I know it gets messy when the
number actually does end in
a long string of zero digits, but one rarely encounters an actual
measurement or value with 15
significant trailing zero digits!)

Regards,

David Mathog
 
S

Seebs

I understand that. Let me rephrase. What is the point of printing
digits with something
other than 0 in them beyond the precision of the numeric
representation in the Mantissa?

This is a much more interesting question.

Suggestion: You might prefer hexadecimal floating point formats. :)

Basically, the problem is that it's not at all obvious how to decide
which base-10 digits corresponded to the precision of the numeric
representation. There's also a secondary issue:
but using bc for the same calculation (arbitrary precision)

so would it not be just as appropriate, and less misleading, for
printf to instead emit:

?

Not really. That's not the number it has stored. If there is one
thing we can be pretty sure of, it's that we don't want printf to print
values OTHER than the ones stored.

Look at it this way: If we adopted such a rule, you would end up with
values where printing them and then reading them in changed them. A lot
more of them than we have now. :)

-s
 
M

Martin Ambuhl

I understand that. Let me rephrase. What is the point of printing
digits with something
other than 0 in them beyond the precision of the numeric
representation in the Mantissa?
They happen to be right for exact powers of 2^128, but certainly are
not in general. For instance:
2.0001^128 is

342467103150178572155544986495756533760

but using bc for the same calculation (arbitrary precision)

342467103150173953551119052597257504919

so would it not be just as appropriate, and less misleading, for
printf to instead emit:

34246710315017000000000000000000000000000

The C language is not responsible for protecting idiots from themselves.
The number printing is an accurate statement of the value stored in the
computer. If the innumerate programmer insists on displaying values he
ought to know are meaningless, that's his problem.
 
I

Ike Naar

I understand that. Let me rephrase. What is the point of printing
digits with something
other than 0 in them beyond the precision of the numeric
representation in the Mantissa?
They happen to be right for exact powers of 2^128, but certainly are
not in general. For instance:
2.0001^128 is

342467103150178572155544986495756533760

but using bc for the same calculation (arbitrary precision)

342467103150173953551119052597257504919

so would it not be just as appropriate, and less misleading, for
printf to instead emit:

34246710315017000000000000000000000000000

The latter would be more misleading. The 27 trailing zeroes imply
that the number is between 34246710315016999999999999999999999999999
and 34246710315017000000000000000000000000001 which is not correct.
If you want to show that the number has only 14 significant digits, then
use only 14 significant digits, like in

34246710315017 * 10**27
 
D

David Mathog

This is a much more interesting question.

Suggestion:  You might prefer hexadecimal floating point formats.  :)

Basically, the problem is that it's not at all obvious how to decide
which base-10 digits corresponded to the precision of the numeric
representation.  There's also a secondary issue:


Look at it this way:  If we adopted such a rule, you would end up with
values where printing them and then reading them in changed them.

If the number of digits printed is enough to specify all the bits
through the least significant bit in the mantissa won't it result in
the exact same number being stored? That brings up the converse
issue, would any digit changes to the right of that point ever change
the stored value? I don't think so.

Regards,

David Mathog
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,479
Members
44,900
Latest member
Nell636132

Latest Threads

Top