floating point - Raising a float near 1 to an infinite power in Python - Stack Overflow

1.000000000000001 ** float('inf') == float('inf')while1.0000000000000001 ** float(

1.000000000000001 ** float('inf') == float('inf')

while

1.0000000000000001 ** float('inf') == 1.0

What determines the exact threshold here? It seems to be an issue of float precision, where numbers below a certain threshold are considered the same as 1.0. Is there a way to get better precision in Python?

1.000000000000001 ** float('inf') == float('inf')

while

1.0000000000000001 ** float('inf') == 1.0

What determines the exact threshold here? It seems to be an issue of float precision, where numbers below a certain threshold are considered the same as 1.0. Is there a way to get better precision in Python?

Share Improve this question edited Mar 27 at 3:45 Selcuk 59.6k12 gold badges111 silver badges115 bronze badges asked Mar 27 at 3:35 Elan SKElan SK 1392 silver badges11 bronze badges 1
  • Tip, when exploring details about FP objects, assign/print the value in hexadecimal notation as in f"{-0.1:a}" --> '-0x1.999999999999ap-4'. – chux Commented Mar 27 at 18:38
Add a comment  | 

3 Answers 3

Reset to default 5

Floating point numbers are stored in 53 bits:

IEEE 754 binary64 values contain 53 bits of precision, so on input the computer strives to convert 0.1 to the closest fraction it can of the form J/2**N where J is an integer containing exactly 53 bits.

1 / 2**53 is ~1.11e-16, so any decimal part less than that will be rounded down to 0.

It can more easily be demonstrated with:

>>> 1.000000000000001 == 1.0
False
>>> 1.0000000000000001 == 1.0
True

If you need better precision, you can use Decimals:

>>> from decimal import Decimal
>>> Decimal("1.0000000000000001") == Decimal("1.0")
False

As others have said, this is due to IEEE754 floats not being able to represent your constants with the precision you're expecting.

Python provides some useful tools that lets you see what's going on, specifically the math.nextafter method and and decimal module.

The decimal module is useful to see the decimal expansion of a float (i.e. how us humans read/write numbers), e.g.:

from decimal import Decimal as D

a = 1.000000000000001
b = 1.0000000000000001

print(D(a), D(b))

which outputs:

1.0000000000000011102230246251565404236316680908203125 1

these are the actual values seen by your computer when you use those constants. As others have said, you can see that b is actually just 1 so it's "lost" the tailing digit you entered. You just have to know that it's going to do that as a programmer.

To see nearby values the nextafter method is very useful, e.g.:

import math

c = math.nextafter(1.0, math.inf)
print(c, D(c))

which outputs:

1.0000000000000002 1.0000000000000002220446049250313080847263336181640625

the first number is what Python prints by default and only includes enough precision to be able to unambiguously distinguish the value, the second uses the decimal module and is exact.

Having a play with these is a great way to further understand what you're actually telling your computer to do. Chux also talked about hex representations of floats, in my experience these take a bit longer for people to understand but are another tool to know about when your code seems to be misbehaving. In Python you use float.hex() to see the actual bits that make up a float, using the format defined by C99, e.g.:

one = 1.0
print(one.hex(), c.hex(), math.nextafter(c, math.inf).hex())

which will output:

0x1.0000000000000p+0 0x1.0000000000001p+0 0x1.0000000000002p+0

This output is closer to how your computer represents floats. The hexadecimal fractional digits can make these values somewhat awkward to interpret, but you can get there with some more magic numbers (i.e. these come from the spec). The second is saying something like 0x10000000000001 * 2**(0-52), the -52 is needed to "shift" the all but the first digit right into the fraction.

Text "1.000000000000001" is converted into a binary64 encoded float. This value is not exactly representable. The 2 closest choices are shown below in decimal and hexadecimal formats. The closer one is used and is more than 1.0 so ... ** float('inf') has an infinite result.

//                          v-------------v  53 bits
 1.0000000000000008882... 0x1.0000000000004p+0 
"1.000000000000001"
 1.0000000000000011102... 0x1.0000000000005p+0  // Closer

Text "1.0000000000000001" is converted into a binary64. This value is not exactly representable either. The 2 closest choices are shown below. The closer one is used and is exactly 1.0 so ... ** float('inf') has a finite result of 1.0.

 1.0000000000000000000    0x1.0000000000000p+0 // Closer 
"1.0000000000000001"
 1.0000000000000002220... 0x1.0000000000001p+0

What determines the exact threshold here?

Consider the binary64 just above and below 1.0

0.99999999999999988898... 0x1.fffffffffffffp-1 
1.0000000000000000000     0x1.0000000000000p+0 
1.0000000000000002220...  0x1.0000000000001p+0 

... and look at the value half-way between 1.0 and that next value:

//                        Using an extended FP type
0.99999999999999994449... 0x1.fffffffffffff_8p-1
1.0000000000000000000     0x1.0000000000000_0p+0 
1.0000000000000001110...  0x1.0000000000000_8p+0

So "text" values about the [0.99999999999999994449... to 1.0000000000000001110...] range will convert exactly to a binary64 encoded 1.0. With ... ** float('inf') will have a finite result of 1.0.

Notice the delta above 1.0 is twice the delta below 1.0. This is due to the ULP of the values changes at 1.0.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744114243a4559096.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信