>>> round(0.45,1) 0.5 >>> round(1.45,1) 1.4 >>> round(2.45,1) 2.5 >>> round(3.45,1) 3.5 >>> round(4.45,1) 4.5 >>> round(5.45,1) 5.5 >>> round(6.45,1) 6.5 >>> round(7.45,1) 7.5 >>> round(8.45,1) 8.4 >>> round(9.45,1) 9.4
接受的答案证实这是由于浮动体的二进制表示是不准确的,这是全部逻辑的.
假设Ruby浮动与Python的不一样,Ruby如何像人类一样漂浮? Ruby欺骗吗
1.9.3p194 :009 > 0.upto(9) do |n| 1.9.3p194 :010 > puts (n+0.45).round(1) 1.9.3p194 :011?> end 0.5 1.5 2.5 3.5 4.5 5.5 6.5 7.5 8.5 9.5
解决方法
这两个实现都是面对同样的issues surrounding binary floating point numbers.
Ruby通过简单的操作直接对浮点数进行操作(乘以10的幂,调整和截断).
Python使用David Gay的复杂算法将二进制浮点数转换为字符串,该算法产生与二进制浮点数完全相等的最小十进制表示.这不会做任何额外的四舍五入,它是一个字符串的精确转换.
使用最短的字符串表示形式,Python将使用精确的字符串操作循环到适当的小数位数.浮点数转换的目标是尝试“撤消”一些二进制浮点表示错误(即如果输入6.6,则在6.6进行循环,而不是6.5999999999999996).
此外,Ruby与一些版本的Python在四舍五入模式中是不同的:从零开始到半数之间.
详情
Ruby不欺骗.它以简单的旧的二进制浮点数开头,Python是相同的.因此,它受到一些同样的挑战(这样的3.35略高于3.35,4.35表示略低于4.35):
>>> Decimal.from_float(3.35) Decimal('3.350000000000000088817841970012523233890533447265625') >>> Decimal.from_float(4.35) Decimal('4.3499999999999996447286321199499070644378662109375')
这是一个链接到Ruby源代码:https://github.com/ruby/ruby/blob/trunk/numeric.c#L1587
Python源码从这里开始:http://hg.python.org/cpython/file/37352a3ccd54/Python/bltinmodule.c
并在这里完成:http://hg.python.org/cpython/file/37352a3ccd54/Objects/floatobject.c#l1080
后者有一个广泛的评论,揭示了两个实现之间的差异:
The basic idea is very simple: convert and round the double to a
decimal string using _Py_dg_dtoa,then convert that decimal string
back to a double with _Py_dg_strtod. There’s one minor difficulty:
Python 2.x expects round to do round-half-away-from-zero,while
_Py_dg_dtoa does round-half-to-even. So we need some way to detect and correct the halfway cases.Detection: a halfway value has the form k * 0.5 * 10**-ndigits for
some odd integer k. Or in other words,a rational number x is exactly
halfway between two multiples of 10**-ndigits if its 2-valuation is
exactly -ndigits-1 and its 5-valuation is at least
-ndigits. For ndigits >= 0 the latter condition is automatically satisfied for a binary float x,since any such float has nonnegative
5-valuation. For 0 > ndigits >= -22,x needs to be an integral
multiple of 5**-ndigits; we can check this using fmod. For -22 >
ndigits,there are no halfway cases: 5**23 takes 54 bits to represent
exactly,so any odd multiple of 0.5 * 10**n for n >= 23 takes at least
54 bits of precision to represent exactly.Correction: a simple strategy for dealing with halfway cases is to
(for the halfway cases only) call _Py_dg_dtoa with an argument of
ndigits+1 instead of ndigits (thus doing an exact conversion to
decimal),round the resulting string manually,and then convert back
using _Py_dg_strtod.
简而言之,Python 2.7非常愿意准确地遵循round-away-from-zero规则.
在Python 3.3中,同样重要的是准确地遵循round-to-even规则.
以下是_Py_dg_dtoa功能的一些额外的细节. Python调用float到string函数,因为它实现了一个算法,可以在相同的替代方案中给出最短的字符串表示.例如,在Python 2.6中,数字1.1显示为1.1000000000000001,但在Python 2.7及更高版本中,它只是1.1. David Gay’s sophisticated dtoa.c algorithm给出了“人们期待的结果”,而不用准确.
该字符串转换算法倾向于弥补对二进制浮点数的round()的任何实现的一些问题的弥补(即4.35开始的4.35而不是4.3499999999999944448662109375).
这和舍入模式(二分之一偶数与零四分之一)是Python和Ruby round()函数之间的本质区别.