round() is not rounding on SF6

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
User avatar
devnull
Posts: 473
Joined: Sat Jan 07, 2017 1:52 am
Location: Singapore / Cornwall
Contact:

round() is not rounding on SF6

Post by devnull » Fri Nov 01, 2019 12:10 pm

What am I doing wrong, I would expect 65.5 ?!

Code: Select all

>>> round(65.50000000000001,1)
65.50000000000001
>>>
Last edited by devnull on Fri Nov 01, 2019 12:25 pm, edited 1 time in total.

kevinkk525
Posts: 969
Joined: Sat Feb 03, 2018 7:02 pm

Re: round() is not rounding

Post by kevinkk525 » Fri Nov 01, 2019 12:12 pm

Are you using unix port?
There it doesn't work, can confirm that.

On my esp8266 it rounds to 65.5
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

User avatar
devnull
Posts: 473
Joined: Sat Jan 07, 2017 1:52 am
Location: Singapore / Cornwall
Contact:

Re: round() is not rounding

Post by devnull » Fri Nov 01, 2019 12:19 pm

@kevinkk525 - No, this is a SF6 Pyboard D !

kevinkk525
Posts: 969
Joined: Sat Feb 03, 2018 7:02 pm

Re: round() is not rounding on SF6

Post by kevinkk525 » Fri Nov 01, 2019 1:55 pm

I remember having had the same problems on esp32 loboris fork and that was due to that fork having float values of double precision (i think it was 8 byte instead of 4 bytes that the esp8266 uses).
I then used an ugly workaround of converting the float to a string of the size I wanted and then sending it as a string through mqtt :D

But you just won't get your expected result of 65.50 because of the float type:

Code: Select all

float(("{0:.2f}").format(value))
65.50000000000001
>>> ("{0:.2f}").format(value)
'65.50'
I know that it would work correctly on CPython but I'm not sure if it's possible in micropython or which workarounds there are.
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: round() is not rounding on SF6

Post by pythoncoder » Sat Nov 02, 2019 7:38 am

We covered this on GitHub. It is something of a FAQ and is general to all programming languages. It's also caused grief to the writers of spreadsheets. Users howl because they see errors so spreadsheets have all sorts of hacks to avoid frightening the horses. So here is my attempt at explaining something which first baffled me in ~1969.

Floats are data types which cannot be represented perfectly in any finite number of binary bits. This is obvious in the case of numbers like π or e. The result of 1/3 requires an infinite number of bits to represent it. Representing floats in 32 or 64 bits will therefore often produce results with evident imprecision and sometimes this is surprising.

If I issue

Code: Select all

>>> round(65.506,2)
65.51000000000001
the number is being correctly rounded to two decimal places, but the resultant value of 65.51 cannot precisely be represented in 64 bits. Hence the error in the 14th decimal place.

As Kevin says, this can be worked round using string formatting but there are other consequences. This link is a simple guide to some practical programming consequences. A more academic exposition is here.
Peter Hinch
Index to my micropython libraries.

Christian Walther
Posts: 169
Joined: Fri Aug 19, 2016 11:55 am

Re: round() is not rounding on SF6

Post by Christian Walther » Sat Nov 02, 2019 8:59 am

That doesn’t explain the OP’s observation to me though, because 65.5, being a multiple of 0.5 = 2^-1, is exactly representable as a float or double. What am I missing?

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: round() is not rounding on SF6

Post by jimmo » Mon Nov 04, 2019 4:39 am

This was also raised on GitHub -- https://github.com/micropython/micropython/issues/5284

Summary (I think): There's a bug in printing when not specifying a number of decimal places, round() is actually doing the correct thing here.

Post Reply