New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
round(1.65, 1) return 1.6 with decimal #69015
Comments
round(1.65, 1) return 1.6 with decimal. >>> 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') |
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 |
I don't agree with "not a bug".
>>> 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. BECAUSE round(decimal.Decimal(s2), ndigits2) and round(v2, ndigits2) |
I think the key point that you're missing (and which I could have made clearer in my previous message) is that 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. |
last compared results are different.
>>> 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 |
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__(). |
In this case.
>>> round(1.65, 1) == 1.7
False
>>> round(2.675, 2) == 2.68
False I never say anything. |
I have a headache. --- 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) |
As Zachary explained, the behavior is correct. There are three issues in play here.
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. |
excuse me. SO SORRY. |
I'm glad you understand it now :) |
Hello, 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. |
Huan, This isn't a bug: see the earlier comments from Zachary Ware on this issue for explanations. When you compute 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 |
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. |
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. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: