In the first case you're actually multiplying by exactly 1:
>>> 1.0000000000000000000000000001
1.0
Binary floating-point is not WYSIWYG - it's subject to rounding back to machine precision at all stages.
Continuing the above:
>>> 1.0000000000000001
1.0
>>> 1.000000000000001
1.000000000000001
So 1.000000000000001
is the smallest literal of this form that doesn't round to exactly 1.0. And then:
>>> sys.float_info.max * 1.000000000000001
inf
Note that it's enough to multiply by the smallest representable float greater than 1, which is a little smaller than that literal's rounded value:
>>> import math
>>> math.nextafter(1.0, 100) * sys.float_info.max
inf
>>> 1.000000000000001 > math.nextafter(1.0, 100)
True
What about addition?
While examples show multiplication, the title of the question asks about addition. So let's do that too. Learn about nextafter()
and ulp()
, which are the right tools with which to approach questions like this. First, an easy computational way to find the largest finite representable float:
>>> import math
>>> big = math.nextafter(math.inf, 0)
>>> big
1.7976931348623157e+308
Now set lastbit
to the value of its least-significant bit:
>>> lastbit = math.ulp(big)
>>> lastbit
1.99584030953472e+292
Certainly if we add that to big
it will overflow to infinity. And so it does:
>>> big + lastbit
inf
However, adding even half that will also overflow, due to "to nearest/even" rounding resolving the halfway tie "up":
>>> big + lastbit / 2.0
inf
Will anything smaller work? No. Anything smaller will just be thrown away by rounding "down". Here we try adding the next smallest representable float, and see that it has no effect:
>>> big + math.nextafter(lastbit / 2.0, 0)
1.7976931348623157e+308