← index #4411Issue #3516
Related · high · value 2.493
QUERY · ISSUE

f presentation type is not rounding

openby qoheniacopened 2019-01-19updated 2024-09-19
needs-info

In MicroPython (tested with version 1.9.3) the f presentation type (as in "{:.0f}".format(x)) is not rounding floating point numbers as CPython (tested with 3.7.2) does. Actually I'm not sure what it really does. It is not rounding, not flooring and not truncating as the following examples show:

print("{:.0f}".format(100.0))
CPython: >>> 100
MicroPython: >>> 100

print("{:.0f}".format(100.9))
CPython: >>> 101
MicroPython: >>> 100

print("{:.0f}".format(101.0))
CPython: >>> 101
MicroPython: >>> 100

print("{:.0f}".format(99.5))
CPython: >>> 100
MicroPython: >>> 99

print("{:.0f}".format(99.6))
CPython: >>> 100
MicroPython: >>> 100
CANDIDATE · ISSUE

Round to the nearest float with the correct least significant decimal.

openby stroobandtopened 2017-12-25updated 2020-05-07
py-core

You have all been very naughty!
Look at the following Christmas bug Santa has brought…

The problem concerns the builtin function round().
In its current implementation, rounding is done to the nearest float that can be represented by the machine.
This is wrong, rounding should be done to the nearest float with the correct least significant decimal that can be represented by the machine.

Consider the following little main.py script:

f = 1011.640071
print('\n%f = float' % f)
print('%f = float rounded to one decimal' % round(f, 1))
print('%6.1f = formatted rounded float\n' % round(f, 1))

delta = 0.000040
print('%f = float correctly rounded to least significant first decimal' % (round(f, 1) + delta))
print('%6.1f = formatted correctly rounded float\n' % (round(f, 1) + delta))

epsilon = abs(7./3 - 4./3 - 1)
print('%f = 1000 × machine epsilon' % (1000 * epsilon))
print(epsilon)
print()

Here is the output on a PyBoard:

MicroPython v1.9.2-87-gda8c4c26 on 2017-09-13; PYBv1.1 with STM32F405RG

1011.639952 = float
1011.599898 = float rounded to one decimal
1011.5 = formatted rounded float

1011.600017 = float correctly rounded to least significant first decimal
1011.6 = formatted correctly rounded float

0.000119 = 1000 × machine epsilon
1.192093e-07

Things are not any different on a Pycom LoPy ESP32:

MicroPython 2ac6da2 on 2017-12-18; LoPy with ESP32

1011.639952 = float
1011.599898 = float rounded to one decimal
1011.5 = formatted rounded float

1011.600017 = float correctly rounded to least significant first decimal
1011.6 = formatted correctly rounded float

0.000119 = 1000 × machine epsilon
1.192093e-07

Note that the difference between correct and erroneous rounding is about a 1000 times the machine epsilon, which makes sense.

For certain applications, such poor rounding can have very serious consequences down the line…

This finding may account for the multitude of other reports complaining about round() behaviour:

  • https://forum.micropython.org/viewtopic.php?t=802
  • https://github.com/bbcmicrobit/micropython/issues/367
  • https://github.com/micropython/micropython/issues/2616
  • https://support.microbit.org/support/solutions/articles/19000026098-strange-rounding-error

Keyboard

j / / n
next pair
k / / p
previous pair
1 / / h
show query pane
2 / / l
show candidate pane
c
copy suggested comment
r
toggle reasoning
g i
go to index
?
show this help
esc
close overlays

press ? or esc to close

copied