【问题标题】:Why is my decimal slightly bigger after coming out of the database?为什么从数据库出来后我的小数会稍微大一点?
【发布时间】:2012-09-30 07:37:45
【问题描述】:

我有点困惑,一个 2 位小数在进出数据库后会额外增加 0.000000000000000001(左右)。

这就是我所做的:(Rails 3.2.8)

创建迁移:

class CreateItems < ActiveRecord::Migration
  def change
    create_table :items do |t|
      t.column :price, :decimal, :precision => 16, :scale => 2
    end
  end
end

创建了一个模型:

class Item < ActiveRecord::Base
end

然后:

$ rails c
>> i = Item.new
>> i.price = 9.46
>> i.save
>> Item.first.price
=> #<BigDecimal:46b3768,'0.9460000000 000001E1',27(45)>

这是一个 SQLite 数据库,里面看起来一切正常:

$ rails db
>> select * from items;
1|9.46

请注意,我注意到发生这种情况的唯一数字是 9.46。 额外的 0.00000000000001 是从哪里来的?


编辑 我知道,如果没有小错误,某些数字的浮点表示是不可能的。但是为什么Item.first.price 不等于BigDecimal.new('9.46')? SQLite 是否存储一个浮点数而不是一个整数,并且它应该被除以 10 的次数(这就是我对小数列的期望)?或者 ActiveRecord 中是否有一些我不知道从数据库中检索值的问题?见下文:

$ rails c
>> decimal = BigDecimal.new('9.46')
>> Item.first.price == decimal
=> false

【问题讨论】:

标签: ruby-on-rails ruby sqlite activerecord


【解决方案1】:

我对 ruby​​ 不是很熟悉,但不会使用

i.price = 9.46

尝试使用

i.price = BigDecimal.new('9.46')

【讨论】:

  • 但是float 9.46在存入数据库的时候肯定已经转成小数了吧?
  • 是的,但由于转换将来自浮点值,因此不会完全是 9.46
  • 肯定不能将 9.460000000000001 存储在数据库中的价格列中,该列已定义为两位数?
  • 这真的取决于数据存储实现。
  • 使用i.price = BigDecimal.new('9.46')i.price = 9.46的效果相同,如果对象被保存然后从数据库中检索
【解决方案2】:

9.46 不能完全表示为 IEEE double-precision 浮点数。这有a great many consequences,但这意味着你得到的值是完全可表示的并且最接近你输入的值。

如果获取 '9.46' 至关重要,请将其存储在具有TEXT 亲和力的列中(并注意您存储的是字符串,而不是“数字”)。

【讨论】:

  • 我还从小道消息中听说,SQLite4 可能有十进制算法来减少这类问题的影响,但它不会影响这个 exact 问题,因为这是造成的因为它需要无数个二进制浮点数才能准确表示 9.46(就像它需要无数个十进制数字才能准确表示 ⅓)。
  • 那么 SQLite3 没有十进制列吗?
  • 我不接受,因为我现在已经意识到我想问的问题是什么,并进行了相应的编辑;我希望这不是糟糕的 SE 礼仪。
【解决方案3】:

我会推荐阅读这篇文章:What Every Computer Scientist Should Know About Floating Arithmetic

文章的 tl;dr 版本...

将无限多个实数压缩为有限位数 需要一个近似的表示。虽然有无限 许多整数,在大多数程序中整数计算的结果可以 以 32 位存储。相反,给定任何固定数量的比特, 大多数实数计算将产生的数量 不能用那么多位精确表示。因此 浮点计算的结果通常必须按顺序四舍五入 以适应其有限的表示。这个舍入误差是 浮点计算的特征。

【讨论】:

  • 看过几次链接;我想是时候真正阅读它了。谢谢
猜你喜欢
  • 2014-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-20
相关资源
最近更新 更多