-2

why round(1.45, 1) gives 1.4 but when I try to round(2.45, 1) it gives 2.5 not 2.4

Adam.Er8
  • 12,675
  • 3
  • 26
  • 38
  • It's a representation error. – Maximouse Jun 27 '19 at 12:10
  • 1
    Hint: `print('{0:.17f}'.format(1.45))` and `print('{0:.17f}'.format(2.45))` See also [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) – glibdud Jun 27 '19 at 12:15
  • 1
    @Georgy Not quite. That one is about banker's rounding, which isn't really the issue here. – glibdud Jun 27 '19 at 12:21
  • @Georgy No, that's still banker's rounding. – glibdud Jun 27 '19 at 12:31
  • There were lots of other similar questions asked before: https://stackoverflow.com/questions/linked/10825926?lq=1 – Georgy Jun 27 '19 at 12:31
  • Also, current answers just reiterate what was said in the answers of the first duplicate target. So I'm still convinced this question should be closed. – Georgy Jun 27 '19 at 12:38

2 Answers2

3

The reason is how floating points are stored internally. A float is rounded to its next valid value, according to IEEE 754.

print("{:.30f}".format(1.45))
print("{:.30f}".format(2.45))
1.449999999999999955591079014994
2.450000000000000177635683940025

Why those values are then rounded to 1.4 and 2.5 should be obvious.

The biggest issue here is how humans think vs how computers calculate: Humans think in base 10, computers in base 2. In base 2, 1.45 is a number with infinite digits, and therefore will be rounded.

If you want to force the computer to think in base 10, you can use the Decimal library:

from decimal import Decimal                                                      

print("{:.30f}".format(Decimal('1.45')))                                         
print("{:.30f}".format(Decimal('2.45')))                                         
print(round(Decimal('1.45'),1))                                                  
print(round(Decimal('2.45'),1))                                                  
1.450000000000000000000000000000
2.450000000000000000000000000000
1.4
2.4

This still might not be what you want, as in math we get taught that rounding 1.45 would result in 1.5. You can change the rounding behaviour in decimal like this:

import decimal                                                                                                                                                 
decimal.getcontext().rounding = decimal.ROUND_HALF_UP                            
1.450000000000000000000000000000
2.450000000000000000000000000000
1.5
2.5

Keep in mind, however, that the computer can calculate a lot faster in base 2 than in base 10.

Finomnis
  • 18,094
  • 1
  • 20
  • 27
  • Actually in math most people get taught to round to the next even number if you are rounding at 0.5. So 1.5 and 2.5 both round to 2. This is because the .5 is exactly in the middle so with transactions where the total came to $xx.xy5 rounding would depend on the value of y giving a ~50/50 / fair chance to round up or down the last penny. – alexanderhurst Jun 27 '19 at 14:49
  • You can read [Bankers Rounding](http://wiki.c2.com/?BankersRounding) for a more complete explanation – alexanderhurst Jun 27 '19 at 14:51
1

you can use decimal module, as it proclaims:

The decimal module provides support for fast correctly-rounded decimal floating point arithmetic.

try this:

from decimal import Decimal

n1_45 = Decimal('1.45')
n2_45 = Decimal('2.45')

print(round(n1_45, 1), round(n2_45, 1))

Output:

1.4 2.4

Adam.Er8
  • 12,675
  • 3
  • 26
  • 38