【问题标题】:floating point precision in ruby on rails model validationsruby on rails 模型验证中的浮点精度
【发布时间】:2010-05-04 16:09:25
【问题描述】:

我正在尝试使用正则表达式验证美元金额: ^[0-9]+\.[0-9]{2}$

这很好用,但是当用户提交表单并且美元金额以 0(零)结尾时,ruby(或 rails?)会将 0 去掉。 所以 500.00 变成 500.0 从而导致正则表达式验证失败。

有没有办法让 ruby​​/rails 保持用户输入的格式,不管尾随零如何?

【问题讨论】:

  • 您为什么不使用validates_numericality_of 而不是正则表达式?
  • 我将如何使用validates_numericality_of 来确保 500.001 会失败?我只想要特别格式化的 '[任何 0 到 9 位数字]。[任何 0 到 9 位数字中的 2 个]' - 因此使用 validates_format_of :amount, :with => /^[0-9]+\.[0-9]{2}$/, :message => "must contain dollars and cents, seperated by a period"

标签: ruby-on-rails ruby validation floating-point


【解决方案1】:

我认为您的美元金额是十进制类型。因此,用户在字段中输入的任何值都会在保存到数据库之前从字符串转换为适当的类型。验证适用于已转换为数字类型的值,因此正则表达式在您的情况下并不是真正合适的验证过滤器。

不过,您有两种可能解决这个问题:

  1. 使用validates_numericality_of。这样您就可以将转换完全留给 Rails,只需检查金额是否在给定范围内。
  2. 使用 validate_each 方法并自己编写验证逻辑(例如检查值是否超过 2 个十进制数字)。
  3. 验证attribute before it's been typecasted:

这在 验证情况,其中用户 可能为整数提供字符串 字段,并且您想要显示 原始字符串返回错误 信息。访问属性 通常会将字符串类型转换为 0,这不是你想要的。

所以,在你的情况下,你应该能够使用:

validates_format_of :amount_before_type_cast, :with => /^[0-9]+\.[0-9]{2}$/, :message => "must contain dollars and cents, seperated by a period"

但是请注意,用户可能会发现遵循您严格的输入规则很乏味(例如,我更希望能够输入 500 而不是 500.00),并且在某些语言环境中,句点不是小数分隔符(如果您打算将您的应用国际化)。

【讨论】:

  • ...:amount_before_type_cast... 是否仍会导致将“500.0”插入数据库?通过验证是一回事,但经理们仍然可以调出付款记录,尽管用户输入正确,他们仍会读取“500.0”?请参阅对 Arkku 的回复,刚性/严格性是应要求提供的。
  • 您(尤其是经理)不应该关心值在数据库中的存储方式。它是一个数字,它应该存储为一个数字(但是,不同的数据库中有许多数字格式)。只有在格式化输出时才应指定小数位数,即在您的数据库视图中。
  • 哦,请永远永远考虑将 DB 列类型更改为字符串。
  • 谢谢。我真的开始考虑将列更改为字符串lol ...好的,所以我将使用before_type_cast 进行验证,然后我将使用number_to_currency 格式化视图-我知道经理不在乎数据库后面是什么,但我想通过存储为视图预先格式化的数字来保存额外的格式化步骤。感谢您的帮助和指导!
【解决方案2】:

一般来说,如果您希望“记住”浮点值的十进制精度,您应该使用 decimal 类型,而不是二进制浮点数。

另一方面,我不确定您为什么希望以如此严格的方式强制字符串表示......如何接受任何数字并使用例如格式化它number_to_currency?

【讨论】:

  • 这个应用程序的会计部门的首席财务官要求刚性。他实际上希望人们以这种格式准确地输入表格。 @payment.amount = number_to_currency(@payment.amount, :unit => '')@payment.save 之前(通过控制器)产生相同的结果......我做错了吗?
  • 恐怕你是... number_to_currency 用于演示目的,即在视图中输出金额时使用。顺便说一句,如果规则如此严格,你可能会考虑做一些客户端验证,我相信有很多基于 jQuery 和原型的解决方案。
【解决方案3】:

通常用钱最好将其存储为以美分为单位的整数(500 美分等于 5.00 美元)。我使用Money gem 来处理这个问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 2014-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-22
    • 1970-01-01
    相关资源
    最近更新 更多