为什么 Ruby 的 Rational class 将字符串参数与数字参数区别对待?

why does Ruby's Rational class treat string arguments differently from numeric arguments?

我正在使用 ruby 的 Rational 库将图像的宽度和高度转换为纵横比。

我注意到字符串参数与数字参数的处理方式不同。

>> Rational('1.91','1')
=> (191/100)
>> Rational(1.91,1)
=> (8601875288277647/4503599627370496)

>> RUBY_VERSION
=> "2.1.5"
>> RUBY_ENGINE
=> "ruby"

仅供参考 1.91:1 是 Facebook 为其平台上的图片推荐的纵横比。

像 191 和 100 这样的值比 8601875288277647 和 4503599627370496 更便于存储在我的数据库中。但我想在决定使用哪种方法之前了解这种差异的来源。

The Rational test suite 似乎没有涵盖这个确切的案例。

Float 1.91 存储为具有给定精度的双精度数,受二进制表示形式的限制。等效的 Rational 对象尽可能保持这种精度,因此它很大。没有办法将 1.91 准确地存储在双精度数中,但您获得的值对于大多数用途来说已经足够接近了。

至于字符串,它代表一个不同的值——确切的值 1.91——并且当你创建一个 Rational 时它会更好地保留它。它比 Float 更正确,UT 用于计算需要更长的时间。

这类似于 1.0/3 的问题,因为它 "goes on forever" 0.333333...等,但 Rational 可以准确地表示它。

免责声明:这只是一个有根据的猜测,基于对如何实现这一壮举的一些了解。

正如 Kent Dahl 所说,浮点数并不精确,它们有固定的精度,这意味着 1.91 实际上是 1.910000000000000000001 或类似的东西,ruby "knows" 应该显示为 1.91。

另一方面,“1.91”是一个字符串,基本上是一个字符数组:'1'、'.'、'9'、'1'。

这就是说,这是你需要做的,从浮点数中构建理性:

  1. 摆脱 . (数学上通过将分子和分母乘以 10^x,或乘以 10 的次数,因为 . 后面有数字。)
  2. 求最大公分母 (gcd)
  3. 用 gcd 划分 num 和 denom

然而,第 1 步对于 Float 和 String 略有不同:

  • Float,我们将不得不乘以 10^x,其中 x(因为精度)不是 2(正如人们认为的 1.91),而是更多的东西,比如 16(记住:1.9100 ... 1).
  • 对于字符串,我们可以将其转换为浮点数并执行相同的技巧,但是嘿,还有更简单的方法:我们只需计算点后面的字符数(即 2),然后删除点将denom乘以10^2...这不仅更简单,而且更精确。

当应用步骤 3 时,大数字可能会再次消失,这就是为什么在处理浮点数的有理数时不会总是得到那些奇怪的结果。

TLDR:数字将根据参数为 String 或 FLoat 以不同方式构建。 FLoats 可以产生很长的数字,因为精度。