Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't float() throw an exception when the argument is outside the range of a Python float?

I'm using Python 3.10 and I have:

a = int(2 ** 1023 * (1 + (1 - 2 ** -52)))

Now, the value of a is the biggest integer value in double precision floating point format.

So, I'm expecting float(a + 1) to give an OverflowError error, as noted in here:

If the argument is outside the range of a Python float, an OverflowError will be raised.

But, to my surprise, it doesn't throw the error, instead, it happily returns:

1.7976931348623157e+308

which seems like sys.float_info.max.

I also do float(a + 2), float(a + 3), float(a + 4), etc but it still returns 1.7976931348623157e+308. Only until I do float(a + a) then it throws the expected exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: int too large to convert to float

It seems like the smallest number that fails is a + 2 ** 970, as noted in this comment.

So, what could be the reason for this?

like image 562
justANewb stands with Ukraine Avatar asked Oct 17 '25 14:10

justANewb stands with Ukraine


1 Answers

As was commented, 2**970 is the smallest addition that rounds up. This makes sense as follows:

 1023 =exp
-  52 =n, where the "smallest" field is 2**-n
-----
  971
                                        L G RS
so, the largest is: (2**1023)*1.111...111(1)00
                                123...012 3 45  // bit #s
                                ^     555 5 55
                          2**1022       ^ ^
                                        ^ ^
                                  2**-971 ^
                                          ^
                                          ^
                                          2**970

...so adding 2**970 will round up, per the LGRS=1100 values. These are the Least significant, Guard, Round, and Sticky bits from the IEEE 754 spec.

This can be demo'd in python as follows:

>>> import sys
>>> print("%100.10f" % (sys.float_info.max))
179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0000000000
>>> # note that even though this is even in base 10, the significand is odd in binary since the Least significant bit is 1
... 
>>> print("%f" % (sys.float_info.max+2**970))
inf
>>> print("%100.10f" % (sys.float_info.max+2**969))
179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0000000000
>>>