为什么Ruby的Float#round行为与Python不同?

前端之家收集整理的这篇文章主要介绍了为什么Ruby的Float#round行为与Python不同?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
Behavior of “round” function in Python”观察到,Python循环像这样漂浮:
>>> 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()函数之间的本质区别.

猜你在找的Ruby相关文章