Reputation: 83
If I have a floating point number x, which is within the range of [0, 106], is it guaranteed in Python that int(ceil(x)) will be rounded up correctly?
It seems possible from what little I know that ceil may be rounded down leading to an incorrect result. Something like: x = 7.6, ceil(x)=7.999.., int(ceil(x))=7. Can that happen?
Upvotes: 3
Views: 426
Reputation: 308392
It's often the case that there's rounding errors in floating point numbers, but that's only because not all numbers that are reprensentable in decimal are perfectly represented in binary. Those numbers that can be represented exactly won't have any rounding applied. That is the case for integer values up to 2**53, so with only 6 digits you will be safe.
The lowest positive integer value that can't be represented exactly in a float
is 9007199254740993.
Upvotes: 0
Reputation: 101989
If you want information about python's float
you should check the sys.float_info
function. In particular its documentation makes it quite clear the python float
s act exactly like C float
/double
s and provide the same guarantees. In fact it links to this part of C's standard.
So python's guarantees are the same as C's double. If an integer can be represented exactly as float then int(the_float)
will return the correct integer.
The problem is that for big enough float
s not all integers can be represented exactly.
Rob's example:
int(math.ceil(18014398509481985.5)) => 18014398509481984
Is not a failure of int
or ceil
, but simply the fact that 18014398509481985.5
can not be represented exactly:
>>> 18014398509481985.5
1.8014398509481984e+16
In summary: if the ceiling of x
can be represented exactly then int(ceil(x))
will return the correct integer. Otherwise int(x)
will return the integer returned by ceil(x)
which need not be the "mathematical ceiling of x
".
Currently, almost all computers conform to IEEE 754 and so you can be sure that int(ceil(x))
returns the correct result according to the standard.
In embedded systems this might not be true but I'm not sure that python can even run on not-IEEE 754 compliant architectures.
If you do not trust what we can say you can always try to read the source code of PyLong_FromDouble
in Objects/longobject.c
:
PyObject *
PyLong_FromDouble(double dval)
{
PyLongObject *v;
double frac;
int i, ndig, expo, neg;
neg = 0;
if (Py_IS_INFINITY(dval)) {
PyErr_SetString(PyExc_OverflowError,
"cannot convert float infinity to integer");
return NULL;
}
if (Py_IS_NAN(dval)) {
PyErr_SetString(PyExc_ValueError,
"cannot convert float NaN to integer");
return NULL;
}
if (dval < 0.0) {
neg = 1;
dval = -dval;
}
frac = frexp(dval, &expo); /* dval = frac*2**expo; 0.0 <= frac < 1.0 */
if (expo <= 0)
return PyLong_FromLong(0L);
ndig = (expo-1) / PyLong_SHIFT + 1; /* Number of 'digits' in result */
v = _PyLong_New(ndig);
if (v == NULL)
return NULL;
frac = ldexp(frac, (expo-1) % PyLong_SHIFT + 1);
for (i = ndig; --i >= 0; ) {
digit bits = (digit)frac;
v->ob_digit[i] = bits;
frac = frac - (double)bits;
frac = ldexp(frac, PyLong_SHIFT);
}
if (neg)
Py_SIZE(v) = -(Py_SIZE(v));
return (PyObject *)v;
}
the code isn't a simple cast because long integers aren't represented as C doubles. And it seems like even in python2 there's no PyInt_FromDouble
.
Upvotes: 1
Reputation: 279305
Python's guarantee about the floating-point format for float
isn't very strict. I think all it says is that it uses double, and in the case of CPython that's whatever the C compiler calls double
.
For numbers up to a million you're fine. No floating-point format in practical use loses precision for integers that small. The C standard requires that double
is OK up to 10 decimal digits.
What you've probably observed is that due to floating-point rounding int(sum([1.1] * 10))
is 10, not 11. That's because sum([1.1] * 10)
is 10.999999999999998
, not 11.0
.
The result of ceil
is always exactly an integer, so it will never be rounded down by int
(or if you like it will be rounded down, but doing so doesn't change it's value!)
Upvotes: 3
Reputation: 223231
A correctly implemented ceil
returns the exact mathematical value of ceil(x), with no error. When IEEE 754, or any reasonable floating-point system, is in use, ceil
is not subject to rounding errors.
This does not prevent adverse effects from sources other than the ceil
function. For example, ceil(1.000000000000000000000000000000001)
will return 1 because 1.000000000000000000000000000000001
is converted to a floating-point value before ceil
is called, and that conversion rounds its result. Similarly, a conversion from double
to float
followed by a call to ceil
may yield a value that is not the ceiling of the original double
value.
The conversion of the result of ceil
to int
of course relies on the range of int
. As long as the value is in range, the conversion should not change the value.
Upvotes: 6