Why is Python's round so strange?
Question:
My code:
#!/usr/bin/python
# -*- coding: utf-8 -*-
print (round(1.555, 1)) # It seems normal
print (round(1.555, 2)) # Why it is not output 1.56?
print (round(1.556, 2)) # It seems normal
Output:
sam@sam:~/code/python$ ./t2.py
1.6
1.55
1.56
sam@sam:~/code/python$
round(1.555, 1)
outputs 1.6
.
Why doesn’t round(1.555, 2)
output 1.56
?
Answers:
Straight from the documentation:
The behavior of round() for floats can be surprising: for example,
round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a
bug: it’s a result of the fact that most decimal fractions can’t be
represented exactly as a float. See Floating Point Arithmetic: Issues
and Limitations for more information.
From http://docs.python.org/2/library/functions.html#round:
Note
The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float. See Floating Point Arithmetic: Issues and Limitations for more information.
Take a look at the documentation:
Note The behavior of round()
for floats can be surprising: for example, round(2.675, 2)
gives 2.67
instead of the expected
2.68
. This is not a bug: it’s a result of the fact that most decimal
fractions can’t be represented exactly as a float. See Floating
Point Arithmetic: Issues and Limitations for more information.
If you keep digging (i.e. click that link), you’ll find an example similar to yours:
The documentation for the built-in round()
function says that it
rounds to the nearest value, rounding ties away from zero. Since the
decimal fraction 2.675
is exactly halfway between 2.67
and 2.68
,
you might expect the result here to be (a binary approximation to)
2.68
. It’s not, because when the decimal string 2.675
is converted
to a binary floating-point number, it’s again replaced with a binary
approximation, whose exact value is
2.67499999999999982236431605997495353221893310546875
String formatting won’t fix your problem either. The floating point number just isn’t stored the way you’d expect it to be:
>>> '{:0.2f}'.format(1.555)
'1.55'
This isn’t really a “fix”, but Python does have a decimal
module, which is designed for floating point arithmetic:
>>> from decimal import Decimal
>>> n = Decimal('1.555')
>>> round(n, 2)
Decimal('1.56')
Using ROUND_CEILING decimal:
>>> from decimal import Decimal, ROUND_CEILING, ROUND_FLOOR
>>> Decimal(1.555).quantize(Decimal('0.01'), rounding=ROUND_CEILING)
> Decimal('1.56')
>>> Decimal(2.675).quantize(Decimal('0.01'), rounding=ROUND_CEILING)
> Decimal('2.68')
>>> Decimal(2.665).quantize(Decimal('0.01'), rounding=ROUND_FLOOR)
> Decimal('2.66')
My code:
#!/usr/bin/python
# -*- coding: utf-8 -*-
print (round(1.555, 1)) # It seems normal
print (round(1.555, 2)) # Why it is not output 1.56?
print (round(1.556, 2)) # It seems normal
Output:
sam@sam:~/code/python$ ./t2.py
1.6
1.55
1.56
sam@sam:~/code/python$
round(1.555, 1)
outputs 1.6
.
Why doesn’t round(1.555, 2)
output 1.56
?
Straight from the documentation:
The behavior of round() for floats can be surprising: for example,
round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a
bug: it’s a result of the fact that most decimal fractions can’t be
represented exactly as a float. See Floating Point Arithmetic: Issues
and Limitations for more information.
From http://docs.python.org/2/library/functions.html#round:
Note
The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float. See Floating Point Arithmetic: Issues and Limitations for more information.
Take a look at the documentation:
Note The behavior of
round()
for floats can be surprising: for example,round(2.675, 2)
gives2.67
instead of the expected
2.68
. This is not a bug: it’s a result of the fact that most decimal
fractions can’t be represented exactly as a float. See Floating
Point Arithmetic: Issues and Limitations for more information.
If you keep digging (i.e. click that link), you’ll find an example similar to yours:
The documentation for the built-in
round()
function says that it
rounds to the nearest value, rounding ties away from zero. Since the
decimal fraction2.675
is exactly halfway between2.67
and2.68
,
you might expect the result here to be (a binary approximation to)
2.68
. It’s not, because when the decimal string2.675
is converted
to a binary floating-point number, it’s again replaced with a binary
approximation, whose exact value is2.67499999999999982236431605997495353221893310546875
String formatting won’t fix your problem either. The floating point number just isn’t stored the way you’d expect it to be:
>>> '{:0.2f}'.format(1.555)
'1.55'
This isn’t really a “fix”, but Python does have a decimal
module, which is designed for floating point arithmetic:
>>> from decimal import Decimal
>>> n = Decimal('1.555')
>>> round(n, 2)
Decimal('1.56')
Using ROUND_CEILING decimal:
>>> from decimal import Decimal, ROUND_CEILING, ROUND_FLOOR
>>> Decimal(1.555).quantize(Decimal('0.01'), rounding=ROUND_CEILING)
> Decimal('1.56')
>>> Decimal(2.675).quantize(Decimal('0.01'), rounding=ROUND_CEILING)
> Decimal('2.68')
>>> Decimal(2.665).quantize(Decimal('0.01'), rounding=ROUND_FLOOR)
> Decimal('2.66')