【发布时间】:2016-11-25 15:02:40
【问题描述】:
小数和浮点数发生了一些非常奇怪的事情,我不明白为什么或在哪里(ruby/rails/postgresql)。
给定一个带有小数列的 purchases 表 - total:
p1 = Purchase.where(total: 5.99).first_or_create
p2 = Purchase.where(total: 5.99).first_or_create
[p1.id, p2.id] # => [1, 2]
p3 = Purchase.where(total: 5.99.to_d).first_or_create
p4 = Purchase.where(total: 5.99.to_d).first_or_create
[p3.id, p4.id] # => [1, 1]
无论是小数还是浮点数,Ruby 和 postgresql 都可以准确表示 5.99:
5.99.to_s # => "5.99"
5.99.to_d.to_s # => "5.99"
5.99 == 5.99.to_d # => true
SELECT CAST(5.99 AS DECIMAL) AS decimal, CAST(5.99 AS FLOAT) AS float;
# decimal | float
# ---------+-------
# 5.99 | 5.99
# (1 row)
SELECT CAST(5.99 AS DECIMAL) = CAST(5.99 AS FLOAT) AS equal;
# equal
# -------
# t
# (1 row)
最重要的是,其他一些值不会发生这种情况:
p5 = Purchase.where(total: 5.75).first_or_create
p6 = Purchase.where(total: 5.75).first_or_create
p7 = Purchase.where(total: 5.75.to_d).first_or_create
[p5.id, p6.id, p7.id] # => [3, 3, 3]
【问题讨论】:
-
基本上,你永远不希望你的
total在任何地方成为浮点值,你会想要说像'5.99'.to_d这样的东西。 -
@muistooshort,实用的解决方案很棒,但用 “不要这样做” 回答 为什么 问题会适得其反。你甚至不知道他们是否是同一个问题。首先,另一个问题以两个不同的数字开头。其次,两位数的精度应该是浮点数可以毫无问题地处理的东西。第三,您可以看到问题出在 Ruby 那里,而在这里我们无法检测到问题(如示例所示)。
-
@muistooshort,请重新打开。我创建这个问题的原因是因为我想更深入地了解正在发生的事情。如果我想要一种让事情正常工作的方法 - 我已经在问题中提供了解决方案。
-
当然,但它几乎肯定是完全相同的潜在问题:
BigDecimal用一些浮点数做了奇怪的事情。 why 部分包含在各种其他浮点问题中。我认为您对“两位数精度”的东西有误,浮点数在内部以二进制表示,并且无论 十进制 精度如何,某些十进制表示都不能以完全精度的二进制表示,您的5.75例子可能是因为3/4的属性,5.99并不是那么简单。 -
没有二进制浮点系统可以准确地表示 5.99。最接近的 IEEE 754 64 位二进制浮点值是 5.9900000000000002131628207280300557613372802734375
标签: ruby-on-rails postgresql activerecord floating-point decimal