BigDecimal(1.8版与1.9版) [英] BigDecimal in 1.8 vs. 1.9

查看:94
本文介绍了BigDecimal(1.8版与1.9版)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

升级到ruby 1.9时,比较浮点数除以BigDecimal的预期值与实际值时,我的测试失败.

When Upgrading to ruby 1.9, I have a failing test when comparing expected vs. actual values for a BigDecimal that is the result of dividing a Float.


expected: '0.495E0',9(18)
got:      '0.4950000000 0000005E0',18(27)

搜索"bigdecimal ruby​​ precision"和"bigdecimal changes ruby​​ 1.9"之类的东西并没有帮助我.

googling for things like "bigdecimal ruby precision" and "bigdecimal changes ruby 1.9" isn't getting me anywhere.

在ruby 1.9中BigDecimal的行为如何变化?

How did BigDecimal's behavior change in ruby 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)>

18(18)和18(45)是什么意思?我想像是精度,但是符号/单位是什么?

What does 18(18) and 18(45) mean? Precision I imagine, but what is the notation/unit?

更新2

代码正在运行:

((10 - 0.1) * (5.0/100)).to_d

我的测试期望它等于(==):

My test is expecting this to be equal (==) to:

0.495.to_f

在1.8下通过,在1.9.2和1.9.3下失败

This passed under 1.8, fails under 1.9.2 and 1.9.3

推荐答案

对于FP值,平等比较很少成功


简短的答案是Float#to_d在1.9中更准确,并且正确地通过了在1.8.7中不应该成功的相等性测试.

Equality comparisons rarely succeed on FP values


The short answer is that the Float#to_d is more accurate in 1.9 and is correctly failing the equality test that should not have succeeded in 1.8.7.

长答案涉及浮点编程的基本规则:从不进行相等比较.相反,建议使用像if (abs(x-y) < epsilon)这样的模糊比较,或者编写代码来避免完全进行相等比较.

The long answer involves a basic rule of floating point programming: never do equality comparisons. Instead, fuzzy comparisons like if (abs(x-y) < epsilon) are recommended, or code is written to avoid the need for equality comparison altogether.

尽管理论上可以精确比较2 32 个单精度数和2 64 个双精度数,但是有一个无限大的数不能被比较如此比较. (注意:对恰好是整数的FP值进行相等比较, 是安全的.因此,与许多建议相反,实际上,它们对于循环索引和下标是绝对安全的)

Although there are in theory about 232 single-precision numbers and 264 double-precision numbers that could be exactly compared, there are an infinite number that cannot be so compared. (Note: it is safe to do equality comparisons on FP values that happen to be integral. So, contrary to much advice, they are actually perfectly safe for loop indices and subscripts.)

更糟糕的是,我们写分数的方式使得与任何特定常数的比较不太可能成功.

Worse, the way we write fractional numbers makes it unlikely that a comparison with any specific constant will be successful.

这是因为分数是二进制的,即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.

That's because the fractions are binary, that is 1/2 + 1/4 + 1/8 ... but our constants are decimal. So, for example, consider monetary amounts in the range $1.00, $1.01, $1.02 .. $1.99. There are 100 values in this range and yet only 4 of them have exact FP representations: 1.00, 1.25, 1.50, and 1.75.

因此,回到您的问题. 0.495的结果没有精确的表示形式,0.1.的输入常数也没有.您可以通过减去两个幅度不同的FP编号来开始计算.较小的数字将被归一化以完成减法,因此它将丢失两个或三个低阶位.结果,该计算将导致比0.495稍大的数字,因为未从10中减去整个0.1.您的常数实际上(内部)比0.495略小.这就是为什么比较失败的原因.

So, back to your problem. Your result of 0.495 has no exact representation and neither does the input constant of 0.1. You begin the calculation with a subtraction of two FP numbers with different magnitudes. The smaller number will be denormalized in order to accomplish the subtraction and so it will lose two or three low-order bits. As a result, the calculation will lead to a slightly large number than 0.495, because the entire 0.1 was not subtracted from 10. Your constant is actually slightly smaller (internally) than 0.495. And that's why the comparison fails.

Ruby 1.8必须偶然或故意丢失了一些低阶位,并有效地引入了舍入步骤,最终对您的测试有所帮助.

Ruby 1.8 must have been accidentally or deliberately losing some low order bits and effectively introducing a rounding step that ended up helping your test.

请记住:经验法则是,您必须在这种舍入中显式编程以进行浮点比较.

Remember: the rule of thumb is that you must explicitly program in such rounding for floating point comparisons.

注释.要从注释中回答有关不具有精确表示形式的简单十进制分数常数的问题:它们没有精确的有限形式,因为它们以二进制形式重复.每个机器分数都是形式为x/2 n 的有理数.现在,常数是十进制的,每个十进制常数是形式为x/(2 n * 5 m )的有理数. 5 m 的数字是奇数,因此任何一个都不存在2 n 的因数.仅当m == 0时,分数的二进制和十进制扩展形式才是有限的表示形式.因此,1.25准确是因为它是5/(2 2 * 5 0 ),而0.1并不是因为它是1/(2 0 * 5 1 ). 根本没有办法将0.1表示为x/2 n 个分量的有限和.

Notes. To answer the question from the comments about simple decimal fraction constants not having exact representations: They don't have exact finite forms because they repeat in binary. Every machine fraction is a rational number of the form x/2n. Now, the constants are decimal and every decimal constant is a rational number of the form x/(2n * 5m). The 5m numbers are odd, so there isn't a 2n factor for any of them. Only when m == 0 is there a finite representation in both the binary and decimal expansion of the fraction. So, 1.25 is exact because it's 5/(22*50) but 0.1 is not because it's 1/(20*51). There is simply no way to express 0.1 as a finite sum of x/2n components.

这篇关于BigDecimal(1.8版与1.9版)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆