[英]Pythonic way to ROUND_HALF_UP float with an arbitrary precision
First of all I would like to mention that this question is not a duplicate of:首先,我想提一下,这个问题不是以下问题的重复:
Python Rounding Inconsistently Python舍入不一致
Python 3.x rounding behavior Python 3.x 舍入行为
I know about IEEE 754 and I know that:我了解 IEEE 754 并且我知道:
The simple "always round 0.5 up" technique results in a slight bias toward the higher number.
简单的“始终向上取整 0.5”技术会导致略微偏向较高的数字。 With large numbers of calculations, this can be significant.
对于大量计算,这可能很重要。 The Python 3.0 approach eliminates this issue.
Python 3.0 方法消除了这个问题。
I agree that ROUND_HALF_UP is inferior method to the one implemented by default in Python.我同意 ROUND_HALF_UP 是 Python 中默认实现的方法的次等方法。 Nevertheless there are people who do not know that and one needs to use that method if the specs require that.
然而,有些人不知道这一点,如果规范要求,人们需要使用这种方法。 Easy way to make this work is:
使这项工作的简单方法是:
def round_my(num, precission):
exp = 2*10**(-precission)
temp = num * exp
if temp%2 < 1:
return int(temp - temp%2)/exp
else:
return int(temp - temp%2 + 2)/exp
But my consideration is that this is not Pythonic ... According to the docs I should use something like:但我的考虑是这不是 Pythonic ......根据文档我应该使用类似的东西:
def round_my(num, pricission):
N_PLACES = Decimal(10) ** pricission # same as Decimal('0.01')
# Round to n places
Decimal(num).quantize(N_PLACES)
The problem is that this would not pass all test cases:问题是这不会通过所有测试用例:
class myRound(unittest.TestCase):
def test_1(self):
self.assertEqual(piotrSQL.round_my(1.53, -1), 1.5)
self.assertEqual(piotrSQL.round_my(1.55, -1), 1.6)
self.assertEqual(piotrSQL.round_my(1.63, -1), 1.6)
self.assertEqual(piotrSQL.round_my(1.65, -1), 1.7)
self.assertEqual(piotrSQL.round_my(1.53, -2), 1.53)
self.assertEqual(piotrSQL.round_my(1.53, -3), 1.53)
self.assertEqual(piotrSQL.round_my(1.53, 0), 2)
self.assertEqual(piotrSQL.round_my(1.53, 1), 0)
self.assertEqual(piotrSQL.round_my(15.3, 1), 20)
self.assertEqual(piotrSQL.round_my(157.3, 2), 200)
Because of the nature of conversion between float and decimal and because quantize does not seem to be working for exponents like 10 or 100. Is there a Pythonic way to do this?由于浮点数和十进制数之间转换的性质,并且因为 quantize 似乎不适用于 10 或 100 等指数。是否有 Pythonic 方法可以做到这一点?
And I know that I could just add infinitesimally small number and round(num+10**(precission-20),-pricission)
would work but this is so wrong that "the puppies would die"...而且我知道我可以添加无穷小的数字和
round(num+10**(precission-20),-pricission)
会起作用,但这是错误的,以至于“小狗会死”......
As you said that doesn't work if you try to quantize
with numbers greater than 1
:正如您所说,如果您尝试使用大于
1
数字进行quantize
,那将不起作用:
>>> Decimal('1.5').quantize(Decimal('10'))
Decimal('2')
>>> Decimal('1.5').quantize(Decimal('100'))
Decimal('2')
But you can simply divide, quantize and multiply:但是您可以简单地进行除法、量化和乘法:
from decimal import Decimal, ROUND_HALF_UP
def round_my(num, precision):
N_PLACES = Decimal(10) ** precision
# Round to n places
return (Decimal(num) / N_PLACES).quantize(1, ROUND_HALF_UP) * N_PLACES
However that only passes the tests if you input Decimal
and compare to Decimal
:但是,如果您输入
Decimal
并与Decimal
进行比较,则只有通过测试:
assert round_my('1.53', -1) == Decimal('1.5')
assert round_my('1.55', -1) == Decimal('1.6')
assert round_my('1.63', -1) == Decimal('1.6')
assert round_my('1.65', -1) == Decimal('1.7')
assert round_my('1.53', -2) == Decimal('1.53')
assert round_my('1.53', -3) == Decimal('1.53')
assert round_my('1.53', 0) == Decimal('2')
assert round_my('1.53', 1) == Decimal('0')
assert round_my('15.3', 1) == Decimal('20')
assert round_my('157.3', 2) == Decimal('200')
As noted in the comments it's possible to use scientific notation decimals as "working" quantize arguments, which simplifies the function:正如评论中所指出的,可以使用科学记数法小数作为“工作”量化参数,这简化了函数:
def round_my(num, precision):
quant_level = Decimal('1e{}'.format(precision))
return Decimal(num).quantize(quant_level, ROUND_HALF_UP)
This also passes the test cases mentioned above.这也通过了上面提到的测试用例。
Here's a version that matches the behavior and API of the builtin round()
function:这是一个与内置
round()
函数的行为和 API 相匹配的版本:
from decimal import Decimal, ROUND_HALF_UP
def round_half_up(number, ndigits=None):
return_type = type(number)
if ndigits is None:
ndigits = 0
return_type = int
if not isinstance(ndigits, int):
msg = f"'{type(ndigits).__name__}' object cannot be interpreted as an integer"
raise TypeError(msg)
quant_level = Decimal(f"10E{-ndigits}")
return return_type(Decimal(number).quantize(quant_level, ROUND_HALF_UP))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.