Issue24827
This issue tracker has been migrated to GitHub,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2015-08-08 03:02 by umedoblock, last changed 2022-04-11 14:58 by admin. This issue is now closed.
Messages (15) | |||
---|---|---|---|
msg248246 - (view) | Author: umedoblock (umedoblock) | Date: 2015-08-08 03:02 | |
round(1.65, 1) return 1.6 with decimal. I feel bug adobe result. not bug ? >>> import decimal >>> d1 = decimal.Decimal("1.65") >>> d2 = decimal.Decimal(10 ** -2) * 5 >>> d1 Decimal('1.65') >>> d2 Decimal('0.05000000000000000104083408559') >>> d1 + d2 Decimal('1.700000000000000001040834086') >>> data = list(map(decimal.Decimal, "1.05 1.15 1.25 1.35 1.45 1.55 1.65 1.75 1.85 1.95".split())) >>> for x in data: ... print("round({}, 1) = {}".format(x, round(x, 1))) ... round(1.05, 1) = 1.0 round(1.15, 1) = 1.2 round(1.25, 1) = 1.2 round(1.35, 1) = 1.4 round(1.45, 1) = 1.4 round(1.55, 1) = 1.6 round(1.65, 1) = 1.6 round(1.75, 1) = 1.8 round(1.85, 1) = 1.8 round(1.95, 1) = 2.0 >>> round(2.675, 2) 2.67 >>> d4 = decimal.Decimal("2.675") >>> round(d4, 2) Decimal('2.68') |
|||
msg248247 - (view) | Author: Zachary Ware (zach.ware) * | Date: 2015-08-08 03:13 | |
The rounding mode of the default context is ROUND_HALF_EVEN[1]: >>> import decimal >>> decimal.getcontext() Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, capitals=1, clamp=0, flags=[], traps=[InvalidOperation, DivisionByZero, Overflow]) For your examples near the end, see [2]: >>> round(2.675, 2) 2.67 >>> round(decimal.Decimal('2.675'), 2) Decimal('2.68') >>> decimal.Decimal(2.675) Decimal('2.67499999999999982236431605997495353221893310546875') >>> round(_, 2) Decimal('2.67') [1] https://docs.python.org/3/library/decimal.html#decimal.ROUND_HALF_EVEN [2] https://docs.python.org/3/tutorial/floatingpoint.html |
|||
msg248249 - (view) | Author: umedoblock (umedoblock) | Date: 2015-08-08 05:38 | |
I don't agree with "not a bug". >>> s1, v1, ndigits1 = "1.65", 1.65, 1 >>> s2, v2, ndigits2 = "2.675", 2.675, 2 >>> decimal.Decimal(v1) Decimal('1.649999999999999911182158029987476766109466552734375') >>> round(v1, ndigits1) 1.6 >>> round(decimal.Decimal(s1), ndigits1) Decimal('1.6') # EQUAL expression round(v1, ndigits1) >>> decimal.Decimal(v2) Decimal('2.67499999999999982236431605997495353221893310546875') >>> round(v2, ndigits2) 2.67 >>> round(decimal.Decimal(s2), ndigits2) Decimal('2.68') # DIFFERENT expression round(v2, ndigits2) decimal module should give me different expression about below. round(decimal.Decimal(s1), ndigits1) and round(v1, ndigits1). BECAUSE round(decimal.Decimal(s2), ndigits2) and round(v2, ndigits2) give me DIFFERENT expression. |
|||
msg248250 - (view) | Author: Zachary Ware (zach.ware) * | Date: 2015-08-08 06:36 | |
I think the key point that you're missing (and which I could have made clearer in my previous message) is that `Decimal(2.675) != Decimal('2.675')`. In the first case, a Decimal instance is created from a float, and 2.675 cannot be represented perfectly in base-2. The float is actually 2.67499999999999982236431605997495353221893310546875, but Python knows you're human and almost certainly didn't want that number, so it shows you 2.675 when asked. The second Decimal instance is created from the string '2.675', and is converted straight to base-10. Moving on to the rounding, both the float 2.675 and the Decimal created from the float 2.675 round down to 2.67 (or nearly, in the case of the float), because they're actually 2.674999..., and 4 rounds down. The Decimal created from a string rounds to 2.68, because it actually is 2.675 and 5 rounds to even (in this case, 8). >>> from decimal import Decimal as D >>> f = 2.675 >>> s = str(f) >>> s # Python chooses the shortest representation '2.675' >>> df = D(f) >>> ds = D(s) >>> f, df, ds (2.675, Decimal('2.67499999999999982236431605997495353221893310546875'), Decimal('2.675')) >>> f == df True >>> f == ds False >>> df == ds False >>> D(round(f, 2)), D(round(df, 2)), D(round(ds, 2)) (Decimal('2.6699999999999999289457264239899814128875732421875'), Decimal('2.67'), Decimal('2.68')) The moral of the story is: everything is working as expected and don't create Decimals from floats unless you want the base-2 approximation of the value. |
|||
msg248251 - (view) | Author: umedoblock (umedoblock) | Date: 2015-08-08 07:11 | |
last compared results are different. should be bug or at least think that how to get a same result about "D(round(df2, 2)) == D(round(ds2, 2))" >>> from decimal import Decimal as D >>> f1 = 1.65 >>> s1 = str(f1) >>> df1 = D(f1) >>> ds1 = D(s1) >>> f2 = 2.675 >>> s2 = str(f2) >>> df2 = D(f2) >>> ds2 = D(s2) >>> f1, df1, ds1 (1.65, Decimal('1.649999999999999911182158029987476766109466552734375'), Decimal('1.65')) >>> f2, df2, ds2 (2.675, Decimal('2.67499999999999982236431605997495353221893310546875'), Decimal('2.675')) >>> D(round(df1, 1)) == D(round(ds1, 1)) True >>> D(round(df2, 2)) == D(round(ds2, 2)) False |
|||
msg248252 - (view) | Author: umedoblock (umedoblock) | Date: 2015-08-08 07:20 | |
In addition. >>> D(round(D("2.675"), 2)) == D("2.68") True >>> D(round(D("1.65"), 1)) == D("1.7") False I believe a bug or at least change the __round__(). |
|||
msg248253 - (view) | Author: umedoblock (umedoblock) | Date: 2015-08-08 07:30 | |
In this case. >>> round(1.65, 1) == 1.7 False >>> round(2.675, 2) == 2.68 False I never say anything. Because I understand what you said. But I use the decimal module. please pay attention to use decimal module. |
|||
msg248256 - (view) | Author: umedoblock (umedoblock) | Date: 2015-08-08 08:45 | |
I have a headache. because python reports many error after I patched below patches. --- Lib/test/test_decimal.py.orig 2015-08-08 17:41:01.986316738 +0900 +++ Lib/test/test_decimal.py 2015-08-08 17:41:05.470316878 +0900 @@ -1935,6 +1935,7 @@ ('123.456', 4, '123.4560'), ('123.455', 2, '123.46'), ('123.445', 2, '123.44'), + ('1.65', 1, '1.7'), ('Inf', 4, 'NaN'), ('-Inf', -23, 'NaN'), ('sNaN314', 3, 'NaN314'), --- ./Lib/decimal.py.orig 2015-08-08 17:42:20.662319881 +0900 +++ ./Lib/decimal.py 2015-08-08 17:39:40.210313472 +0900 @@ -1782,7 +1782,7 @@ def _round_half_even(self, prec): """Round 5 to even, rest to nearest.""" if _exact_half(self._int, prec) and \ - (prec == 0 or self._int[prec-1] in '02468'): + (prec == 0 or self._int[prec-1] in '01234'): return -1 else: return self._round_half_up(prec) |
|||
msg248260 - (view) | Author: Merlijn van Deen (valhallasw) * | Date: 2015-08-08 10:52 | |
As Zachary explained, the behavior is correct. There are three issues in play here. 1) The rounding method. With the ROUND_HALF_EVEN rounding mode, .5 is rounded to the nearest *even* number, so 1.65 is rounded to 1.6, while 1.75 is rounded to 1.8. 2) Rounding of floats. Floats cannot represent every number, and numbers are therefore rounded. - round(2.675, 2) = round(2.6749999999999998, 2) and is thus rounded to 2.67 - round(1.65, 1) = round(1.6499999999999999, 1) and is thus rounded to 1.6 3a) In Python 2, round returns a float, so Decimal(round(Decimal("1.65"))) = Decimal(1.6) = Decimal('1.600000000000000088817841970012523233890533447265625') != Decimal('1.6') 3b) In Python 3, Decimal.__round__ is implemented, so round(D("1.65"), 1) == D("1.6") as expected. |
|||
msg248268 - (view) | Author: umedoblock (umedoblock) | Date: 2015-08-08 13:14 | |
excuse me. I understand ROUND_HALF_EVEN meaning. I think that __round__() function work ROUND_HALF_UP. so sorry. I don't have exactly knowledge about ROUND_HALF_EVEN. I misunderstand about ROUND_HALF_EVEN. I have thought ROUND_HALF_EVEN means ROUND_HALF_UP. SO SORRY. |
|||
msg248309 - (view) | Author: Zachary Ware (zach.ware) * | Date: 2015-08-09 02:27 | |
I'm glad you understand it now :) |
|||
msg299493 - (view) | Author: Huan Wang (Huan) | Date: 2017-07-30 09:00 | |
Hello, I was confused by the decimal module. The problem is that I want to from decimal import Decimal, ROUND_HALF_UP def rounded(number, n): ''' Round the digits after the n_th decimal point by using decimal module in python. For example: 2.453 is rounded by the function of deal_round(2.453, 1), it will return 2.5. 2.453 is rounded by the function of deal_round(2.453, 2), it will return 2.45. ''' val = Decimal(number) acc = str(n) # n = 0.1 or 0.01 or 0.001 return Decimal(val.quantize(Decimal(acc), rounding=ROUND_HALF_UP)) for x in np.arange(1.0, 4.01, 0.01): rounded_val = rounded(x, 0.1) print("{:}\t{:}".format(x, rounded_val)) The results obtained from the numpy array looks fine, but if I directly used rounded(1.45, 0.1), it yielded Decimal('1.4'), rather than Decimal('1.5'). I think it would be a bug. |
|||
msg299497 - (view) | Author: Mark Dickinson (mark.dickinson) * | Date: 2017-07-30 11:27 | |
Huan, This isn't a bug: see the earlier comments from Zachary Ware on this issue for explanations. When you compute `rounded(1.45, 0.1)`, you convert the *float* 1.45 to a Decimal instance. Thanks to the What You See Is Not What You Get nature of binary floating point, the actual value stored for 1.45 is: 1.4499999999999999555910790149937383830547332763671875 Conversion from float to Decimal is exact, so the Decimal value you're working with is also a touch under 1.45: >>> from decimal import Decimal >>> Decimal(1.45) Decimal('1.4499999999999999555910790149937383830547332763671875') And so it correctly rounds down to `1.4`. |
|||
msg299498 - (view) | Author: Huan Wang (Huan) | Date: 2017-07-30 11:45 | |
Hi Mark, Thank you for your reply. I went over again the answer from Zachary Ware published on 2015-08-08 09:36. I got the point that it is better to use string type of number. >>> from decimal import Decimal, ROUND_HALF_UP >>> Decimal("1.45") Decimal('1.45') >>> Decimal(Decimal("1.45").quantize(Decimal("0.1"), rounding=ROUND_HALF_UP)) Decimal('1.5') I think it is better to make a tip in the Python tutorial. |
|||
msg299540 - (view) | Author: Mark Dickinson (mark.dickinson) * | Date: 2017-07-31 07:15 | |
> I think it is better to make a tip in the Python tutorial. I'd recommend opening a separate issue (or pull request, if you're feeling adventurous) for that; this issue is old and closed, and it's unlikely many will be following it. |
History | |||
---|---|---|---|
Date | User | Action | Args |
2022-04-11 14:58:19 | admin | set | github: 69015 |
2017-07-31 07:15:26 | mark.dickinson | set | messages: + msg299540 |
2017-07-30 11:45:01 | Huan | set | messages: + msg299498 |
2017-07-30 11:27:45 | mark.dickinson | set | nosy:
+ mark.dickinson messages: + msg299497 |
2017-07-30 09:00:00 | Huan | set | nosy:
+ Huan messages: + msg299493 versions: + Python 3.5, - Python 3.3 |
2015-08-09 02:27:11 | zach.ware | set | messages: + msg248309 |
2015-08-08 13:14:47 | umedoblock | set | messages: + msg248268 |
2015-08-08 10:52:29 | valhallasw | set | nosy:
+ valhallasw messages: + msg248260 |
2015-08-08 08:45:58 | umedoblock | set | messages: + msg248256 |
2015-08-08 07:30:08 | umedoblock | set | messages: + msg248253 |
2015-08-08 07:20:03 | umedoblock | set | messages: + msg248252 |
2015-08-08 07:11:41 | umedoblock | set | messages: + msg248251 |
2015-08-08 06:36:25 | zach.ware | set | messages:
+ msg248250 title: round(1.65, 1) return 1.6 with decima modulel -> round(1.65, 1) return 1.6 with decimal |
2015-08-08 05:38:41 | umedoblock | set | messages:
+ msg248249 title: round(1.65, 1) return 1.6 with decimal -> round(1.65, 1) return 1.6 with decima modulel |
2015-08-08 03:13:53 | zach.ware | set | status: open -> closed nosy: + zach.ware messages: + msg248247 resolution: not a bug stage: resolved |
2015-08-08 03:02:03 | umedoblock | create |