Ruby float比较错了吗?

时间:2012-12-24 21:15:11

标签: ruby floating-point

我有一个ActiveRecord对象:

 s = Show.find 3980

Show有一个Decimal类型的lng列(精度10,比例7)。如果我在lng上致电s

s.lng
#=> #<BigDecimal:7fac9a12ff40,'-0.821975E2',18(18)> 
一切都很好。但如果我这样做:

s.lng == -82.1975
#=> false

返回false!但他们俩都是一样的!它与我的数据库列有什么关系吗?

2 个答案:

答案 0 :(得分:5)

不要将FPU十进制字符串分数与等式进行比较

所以,在1.8.7p330:

BigDecimal.new('-82.1975') == -82.1975
 => true

但是在1.9.3p194上它是false.

问题在于浮点值或双精度值与包含分数的十进制常量的比较最多是不可靠的。

非常少的十进制字符串分数在二进制FP表示中具有精确值。 * 例如,在0.01和0.99之间,只有0.25,0.50和0.75具有不重复的精确二进制表示。这大约是BigDecimal存在的原因的一半。 (另一半原因是FP数据的固定精度。)

因此,在将实际机器值与十进制字符串常量进行比较时得到的结果取决于二进制分数中的每个位...低至1/2 52 ......即使这样也需要四舍五入。

如果对产生数字,输入转换代码或其他任何相关内容的过程有任何不一致(嘿嘿,有点,抱歉)不完美,那么它们看起来就不一样了。

可以说比较应该总是失败,因为没有IEEE格式的FPU甚至可以准确地表示该数字。

多年来,MySQL和ActiveRecord已经改变了处理分数的确切方式。

但是你肯定不需要将浮动与BigDecimal混合,即选择:

BigDecimal.new('-82.1975')

并与之比较。


* 问题:机器编号为x / 2 n ,但十进制常数为x /(2 n * 5 )。功能

答案 1 :(得分:0)

请尝试s.lng.to_f == -82.1975