on Python 3.6.2, Win10-x64
Just something curious that I encountered and couldn't quite explain.
In:
x = 10.0
for i in range(10):
print(str(i) + " | " + str(x))
x *= x
Out:
0 | 10.0
1 | 100.0
2 | 10000.0
3 | 100000000.0
4 | 1e+16
5 | 1e+32
6 | 1.0000000000000002e+64
7 | 1.0000000000000003e+128
8 | 1.0000000000000005e+256
9 | inf
How come it just turns into inf
? Why doesn't it throw an exception?
If I replace the *=
in the last line with **=
for example, then by the second iteration it raises OverflowError: (34, 'Result too large')
, which makes sense (since it is a mere 10.000.000.000^10.000.000.000).
Does this mean that there is a kind of "soft" limit on floats that -- when exceeded -- turns them into inf
? And if so, what is that limit and is it the same regardless of the arithmetic operation? And wouldn't that imply something like inf == this_limit + anything
?
.
ADD:
I get that there is the sys.float_info.max
. Is that the limit?
I just got an idea and tested some stuff out:
print(sys.float_info.max)
print(sys.float_info.max + 1)
print(sys.float_info.max * 2)
print(sys.float_info.max * 1.000000000000001)
print(sys.float_info.max * 1.0000000000000001)
This gives me:
1.7976931348623157e+308
1.7976931348623157e+308
inf
inf
1.7976931348623157e+308
This is strange to me...
Representing infinity as an Integer in python But in python, as it is a dynamic language, float values can be used to represent an infinite integer. One can use float('inf') as an integer to represent it as infinity.
First off, you can multiply floats. The problem you have is not the multiplication itself, but the original number you've used. Multiplication can lose some precision, but here the original number you've multiplied started with lost precision. This is actually an expected behavior.
Definition and Usageinf constant returns a floating-point positive infinity. For negative infinity, use -math. inf . The inf constant is equivalent to float('inf') .
The int type automatically promotes to long when a computation goes beyond its range, thereby retaining precision. float doesn't get bigger to remain precise.
If you inspect float operations implementation in CPython (floatobject.c
), you can see that most operations are simply deferred to C double
ops, like for example in float_add
, float_sub
, or float_mul
:
static PyObject *
float_mul(PyObject *v, PyObject *w)
{
double a,b;
CONVERT_TO_DOUBLE(v, a);
CONVERT_TO_DOUBLE(w, b);
PyFPE_START_PROTECT("multiply", return 0)
a = a * b;
PyFPE_END_PROTECT(a)
return PyFloat_FromDouble(a);
}
The result, a = a * b
, is calculated on CPU, according to the IEEE 754 standard -- which proscribes the default result of -inf
/+inf
in case of an overflow (i.e. when the result can not be stored within the finite 32-bit float
or 64-bit double
).
Another thing to note is that any of the five floating-point exceptions defined in the IEEE 754 (like overflow) can either (1) produce the default value and store the exception information in the status word, or (2) raise SIGFPE
signal if FP exception traps are enabled (see GNU lib C docs on FP exceptions).
In Python, traps are never enabled, so the program runs in a non-stop mode.
That means, in turn, that the result of an overflow in the float_mul
routine above will default to float inf
(as defined in IEEE 754), and Python will simply return PyFloat_FromDouble(a)
, where a
is inf
.
On the other hand, if we inspect the float_pow
(shortened version below):
static PyObject *
float_pow(PyObject *v, PyObject *w, PyObject *z)
{
...
errno = 0;
PyFPE_START_PROTECT("pow", return NULL)
ix = pow(iv, iw);
PyFPE_END_PROTECT(ix)
Py_ADJUST_ERANGE1(ix);
if (errno != 0) {
PyErr_SetFromErrno(errno == ERANGE ? PyExc_OverflowError :
PyExc_ValueError);
...
}
...
}
we can see the result of your x **= x
would be inf
, if it were not for the additional status word (~ errno
) checking -- and raising of the Python OverflowError
exception in case the underlying pow
overflowed.
In conclusion (as already noted here), Python is not consistent in handling floating point exceptions -- sometimes will return the default (from C/libc
/CPU), and sometimes will raise a Python exception.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With