以任意精度将浮点数四舍五入的python方法
首先,我想提一提的是,这个问题并不是重复的: 我知道IEEE 754,我知道: 简单的“始终向上取整0.5”技术会导致稍微偏向更高的数字。对于大量的计算,这可能非常重要。Python 3.0方法消除了这个问题 我同意ROUND_HALF_UP方法不如Python中默认实现的方法。尽管如此,还是有人不知道这一点,如果规范要求,就需要使用这种方法。实现这一目标的简单方法是:以任意精度将浮点数四舍五入的python方法,python,floating-point,decimal,rounding,Python,Floating Point,Decimal,Rounding,首先,我想提一提的是,这个问题并不是重复的: 我知道IEEE 754,我知道: 简单的“始终向上取整0.5”技术会导致稍微偏向更高的数字。对于大量的计算,这可能非常重要。Python 3.0方法消除了这个问题 我同意ROUND_HALF_UP方法不如Python中默认实现的方法。尽管如此,还是有人不知道这一点,如果规范要求,就需要使用这种方法。实现这一目标的简单方法是: def round_my(num, precission): exp = 2*10**(-precission)
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
问题在于,这不会通过所有测试用例:
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)
由于浮点和十进制之间转换的性质,以及量化似乎对10或100这样的指数不起作用。有没有类似蟒蛇的方法
我知道我可以加上无穷小的数字,而
四舍五入(num+10**(precision-20),-pricision)
会起作用,但这是错误的,以至于“小狗会死”…正如你所说,如果你试图用大于1的数字来计算,那是行不通的:
>>> Decimal('1.5').quantize(Decimal('10'))
Decimal('2')
>>> Decimal('1.5').quantize(Decimal('100'))
Decimal('2')
但您可以简单地进行除法、量化和乘法:
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
但是,只有在输入十进制
并与十进制
进行比较时,才能通过测试:
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')
如评论中所述,可以使用科学符号小数作为“工作”量化参数,从而简化函数:
def round_my(num, precision):
quant_level = Decimal('1e{}'.format(precision))
return Decimal(num).quantize(quant_level, ROUND_HALF_UP)
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))
这也通过了上面提到的测试用例。下面是一个与内置函数的行为和API相匹配的版本:
def round_my(num, precision):
quant_level = Decimal('1e{}'.format(precision))
return Decimal(num).quantize(quant_level, ROUND_HALF_UP)
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))
你知道,例如1.65
不符合“四舍五入”的条件,因为它实际上是1.6499999999911182158029987…
?我知道-这就是为什么我写了:“因为浮点和十进制之间的转换性质”-但你是对的,我应该更精确一些。另一方面,我现在想知道,我可能应该省略浮点数,然后对十进制数执行所有计算……不,这不是问题所在。问题是您根本没有float
s。无法恢复创建浮点的字符串。在您的代码中,将它们作为字符串或小数保存,以避免“一”字陷阱-在您提出您的答案之前,我已将我的答案添加到EDDITE中。。。是的-你是对的。。。您的answear解决了这个问题。使用Decimal('1e1')
而不是Decimal('10')
进行量化应该可以像预期的那样工作。@MarkDickinson似乎可以工作。我有点惊讶,你知道为什么会这样吗?quantize
方法使用第二个参数的指数来确定如何量化。这是一个有点奇怪的设计,但是它是decimal
模块所基于的规范中的内容。(因此,使用Decimal('2e1')
或Decimal('13e1')
进行量化也会起作用。)您的方法总是返回一个Decimal
,而它们可能会返回输入类型。@Boris您总是可以再次将其转换为其他类型。然而,转换十进制->浮动可能会导致精度的损失,所以如果你真的想要这样的话,你应该考虑一下。