当升级到ruby 1.9时,我在比较预期值与实际值之间时遇到了失败的测试,BigDecimal这是划分Float的结果.
expected: '0.495E0',9(18) got: '0.4950000000 0000005E0',18(27)
谷歌搜索"bigdecimal ruby precision"和"bigdecimal changes ruby 1.9"之类的东西并没有让我感到满意.
BigDecimal红宝石1.9中的行为是如何变化的?
更新1
> RUBY_VERSION
=> "1.8.7"
> 1.23.to_d
=> #<BigDecimal:1034630a8,'0.123E1',18(18)>
> RUBY_VERSION
=> "1.9.3"
> 1.23.to_d
=> #<BigDecimal:1029f3988,'0.123E1',18(45)>
Run Code Online (Sandbox Code Playgroud)
18(18)和18(45)是什么意思?精确的我想象,但是符号/单位是什么?
更新2
代码正在运行:
((10 - 0.1) * (5.0/100)).to_d
Run Code Online (Sandbox Code Playgroud)
我的测试期望它与(==)相等:
0.495.to_f
Run Code Online (Sandbox Code Playgroud)
这通过1.8,在1.9.2和1.9.3下失败
Dig*_*oss 17
简短的回答是Float#to_d在1.9中更准确,并且正确地失败了1.8.7中不应该成功的相等测试.
长答案涉及浮点编程的基本规则:永远不要进行相等比较.相反,if (abs(x-y) < epsilon)建议进行模糊比较,或编写代码以避免完全相等的比较.
虽然理论上有2 32个单精度数和2 64个双精度数可以精确比较,但有一个无数的数字无法进行比较.(注意:对FP值进行相等比较是安全的,这恰好是积分.因此,与许多建议相反,它们对于循环索引和下标实际上是完全安全的.)
更糟糕的是,我们编写小数的方式使得与任何特定常数的比较不太可能成功.
这是因为分数是二进制的,即1/2 + 1/4 + 1/8 ......但我们的常数是十进制的.因此,例如,考虑范围内的货币金额此范围内$1.00, $1.01, $1.02 .. $1.99.有100个值,但其中只有4个具有精确的FP表示:1.00, 1.25, 1.50, and 1.75.
所以,回到你的问题.你的结果0.495没有精确的表示,输入常数也没有0.1. 你开始计算时减去两个不同幅度的FP数.较小的数字将被非规范化以便完成减法,因此它将丢失两个或三个低阶位.结果,计算将导致略大于0.495的数字,因为整数0.1未从10中减去.您的常数实际上略小于(内部)0.495.这就是比较失败的原因.
Ruby 1.8必须偶然或故意丢失一些低位,并有效地引入一个舍入步骤,最终帮助您进行测试.
请记住:经验法则是您必须在这种舍入中明确编程以进行浮点比较.
笔记.要回答关于没有精确表示的简单小数分数常量的注释中的问题:它们没有精确的有限形式,因为它们以二进制重复.每个机器分数是x/2 n形式的有理数.现在,常量是十进制的,每个十进制常数是x /(2 n*5 m)形式的有理数.5 米的数字是奇数,因此其中任何一个都没有2 n因子.只有当m == 0时,才能在分数的二进制和十进制扩展中得到有限的表示.因此,1.25是准确的,因为它是5 /(2 2*5 0),但0.1不是因为它是1 /(2 0*5 1).根本没有办法将0.1表示为x/2 n分量的有限和.
| 归档时间: |
|
| 查看次数: |
1507 次 |
| 最近记录: |