Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Weird rounding
#1
Hi everyone.

The post seems long but it won't take much of your time, I promise :)
This is the code:

def frange(start, stop, step):
    
    i = start
    while i <= stop:
        yield i
        i += step

for p in frange(5, 6.9, 0.01):
    print(str(p))
First few print outputs are:
Output:
5 5.01 5.02 5.029999999999999 5.039999999999999 5.049999999999999 5.059999999999999 5.0699999999999985 5.079999999999998 5.089999999999998 5.099999999999998
and all of the rest is the same.

1. Why is this happening?

2. How to work around/with it?

What I have done is

def frange(start, stop, step):
    
    i = start
    while i <= stop:
        i = float("{0:.2f}".format(i))
        yield i
        i += step
and then the print is fine. But that's not my main issue. My main issue happens here:

def gmaxf(d):
    
    if d == 13: return 32
    if d == 16: return 38
    if d == 20: return 40
    if d == 25: return 44
    if d == 32: return 50
    if d == 40: return 56
    return 0

def wminf(d):
    
    if d == 13: return 5
    if d == 16: return 6
    if d == 20: return 8
    if d == 25: return 10
    if d == 32: return 11.5
    if d == 40: return 14
    return 0

for d in diameters:
    pmin = wminf(d)
    pmax = d + 0.09
    gmin = d + 0.1
    gmax = gmaxf(d)
    for p in frange(wminf(d), pmax, 0.01):
        for w in frange(wminf(d), p, 0.01):
            rmin = rminf(p, w, gmin, gmax)
            rmax = rmaxf(p, w, gmin, gmax)
            if rmin > rmax:
                pmin = p + 0.01
    print('D = ' + str(d) + ', Pmin = ' + str(pmin))
And this is the output:

Output:
D = 13, Pmin = 12.2 D = 16, Pmin = 15.03 D = 20, Pmin = 18.540000000000003 D = 25, Pmin = 23.12 D = 32, Pmin = 30.060000000000002 D = 40, Pmin = 37.669999999999995
As you can see, I'm trying to found out minimum possible value for Pmin. rminf and rmaxf do not in any way change any of the variables' values, they simply calculate something and return it. I don't want to just round-up the value for Pmin because I have no idea what is going on. For example, I can assume that Pmin = 30.06 for D = 32. But what the heck is going on with Pmin for D = 40?

From both cases I can see that the problem occurs within p + 0.01. Why? What's going on?

Thank you! :)
Reply
#2
That's just how floating point numbers work. https://floating-point-gui.de/

The simple answers are to either:
- ignore it, and format numbers before printing them, or
- use the Decimal module, which favors precision the way you're expecting: https://docs.python.org/3/library/decimal.html
Reply
#3
(Oct-12-2018, 06:55 PM)nilamo Wrote: That's just how floating point numbers work. https://floating-point-gui.de/

The simple answers are to either:
- ignore it, and format numbers before printing them, or
- use the Decimal module, which favors precision the way you're expecting: https://docs.python.org/3/library/decimal.html

Will precision remain if I do this?

for d in diameters:
    pmin = wminf(d)
    pmax = d + 0.09
    gmin = d + 0.1
    gmax = gmaxf(d)
    for p in frange(wminf(d), pmax, 0.01):
        for w in frange(wminf(d), p, 0.01):
            rmin = rminf(p, w, gmin, gmax)
            rmax = rmaxf(p, w, gmin, gmax)
            if rmin > rmax:
                pmin = float("{0:.2f}".format(p + 0.01))
    print('D = ' + str(d) + ', Pmin = ' + str(pmin))
If I round it up after every 0.01 addition? It should, right?
Reply
#4
No, you lose any semblance of precision if you add random numbers, then cast to a string and back to a float.
Reply
#5
Thank you for your time so far.
Becuase P is always x.yz number (only 2 decimals, or 1, or none), would this be good enough?

pmin = (int(p*100) + 1 ) / 100
That way addition would be done with two integers and the result is simply integer/100.
As you can tell, I am trying to avoid Decimal module if possible :)
Reply
#6
(Oct-12-2018, 07:39 PM)PythonZenon Wrote: As you can tell, I am trying to avoid Decimal module if possible :)
But why?
Any why isn't float good enough? Is it actually acting in a way you don't expect, or is it just displaying poorly when printed?
Reply
#7
The data base at my old job stored all the weights as integers. They were actually calculated to four decimal places. You would do calculations on them and then divide by 10000 for the final result. We would do something similar when checking the register at the sports card store I worked at: You did all your calculations in pennies instead of dollars, so you didn't have to worry about an incorrect decimal.
Craig "Ichabod" O'Brien - xenomind.com
I wish you happiness.
Recommended Tutorials: BBCode, functions, classes, text adventures
Reply
#8
(Oct-12-2018, 07:46 PM)nilamo Wrote: But why?
Any why isn't float good enough? Is it actually acting in a way you don't expect, or is it just displaying poorly when printed?

Honestly, because there is just SO MUCH TEXT I would have to read about Decimal module and I have spent so much time typing this code already. I just want to get it done. If i decide to implement Decimal module in my entire program, it's gonna take even more time because there are additional functions I have not shown here.

I dislike doing it with float because there might be many many additions while running algorithm and the error gets bigger and bigger and I lose precision. If I do it with doing *100 and converting to int, I have rounded-up number in each iteration of 'for' and error doesn't get bigger, right?

(Oct-12-2018, 07:48 PM)ichabod801 Wrote: The data base at my old job stored all the weights as integers. They were actually calculated to four decimal places. You would do calculations on them and then divide by 10000 for the final result. We would do something similar when checking the register at the sports card store I worked at: You did all your calculations in pennies instead of dollars, so you didn't have to worry about an incorrect decimal.

And this is what I shall do as well :) Thank you both!
Reply
#9
Simple modification with round
Output:
In [32]: def frange(start, stop, step, precision=2): ...: while start <= stop: ...: yield start ...: start = round(start + step, precision) ...: list(frange(5, 5.1, .01)) ...: ...: Out[32]: [5, 5.01, 5.02, 5.03, 5.04, 5.05, 5.06, 5.07, 5.08, 5.09, 5.1]
Actually, you may even caclulate precision "on the fly"
Output:
In [36]: def frange(start, stop, step): ...: precision = len(str(step).partition('.')[-1]) ...: while start <= stop: ...: yield start ...: start = round(start + step, precision) ...: print(list(frange(5, 10, 1.1)), list(frange(5, 10, 1)), sep='\n') ...: ...: [5, 6.1, 7.2, 8.3, 9.4] [5, 6, 7, 8, 9, 10]

(Oct-12-2018, 06:37 PM)PythonZenon Wrote:
def gmaxf(d):
    
    if d == 13: return 32
    if d == 16: return 38
    if d == 20: return 40
    if d == 25: return 44
    if d == 32: return 50
    if d == 40: return 56
    return 0

There's a better way to implement that in Python, Pythonic way - by using dict instead of long chain of if's
def gmaxf(d):
    return {13: 32, 16: 38,....}.get(d, 0)
Test everything in a Python shell (iPython, Azure Notebook, etc.)
  • Someone gave you an advice you liked? Test it - maybe the advice was actually bad.
  • Someone gave you an advice you think is bad? Test it before arguing - maybe it was good.
  • You posted a claim that something you did not test works? Be prepared to eat your hat.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  need help rounding joseph202020 7 1,308 Feb-21-2023, 08:13 PM
Last Post: joseph202020
  from numpy array to csv - rounding SchroedingersLion 6 2,153 Nov-14-2022, 09:09 PM
Last Post: deanhystad
  Random data generation sum to 1 by rounding juniorcoder 9 3,409 Oct-20-2021, 03:36 PM
Last Post: deanhystad
  Rounding issue kmll 1 1,403 Oct-08-2021, 10:35 AM
Last Post: Yoriz
  Not rounding to desired decimal places? pprod 2 2,537 Mar-05-2021, 11:11 AM
Last Post: pprod
  Decimal Rounding error project_science 4 2,734 Jan-06-2021, 03:14 PM
Last Post: project_science
  rounding and floats Than999 2 3,076 Oct-26-2020, 09:36 PM
Last Post: deanhystad
  Rounding to the nearest eight wallgraffiti 2 2,062 Jul-15-2020, 06:05 PM
Last Post: wallgraffiti
  rounding question DPaul 16 5,515 Apr-12-2020, 02:30 PM
Last Post: DPaul
  price + tax rounding mlieqo 11 6,406 Sep-21-2019, 04:53 PM
Last Post: mlieqo

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020