【问题标题】:Multiplication in Ruby causing overflow?Ruby中的乘法导致溢出?
【发布时间】:2012-11-06 20:14:17
【问题描述】:

所以,我从理论上读到,Ruby 中的整数没有最大值,它们可以任意大。然而,当我乘以下面的结果是一个负值(我认为这是溢出的标志?):

44404051714 * 44404051714 => -2081807267335685116

谁能给我解释一下?

【问题讨论】:

  • 嗯,在这里工作:irb(main):001:0> 44404051714 * 44404051714 => 1971719808619586337796
  • 我也在使用 irb - 你使用的是哪个版本的 Ruby?
  • ruby 1.8.7 (2011-12-28 patchlevel 357) [x86_64-linux]
  • 嗯。看起来像一个错误,无论是在 Ruby 的实现中还是在用于编译 Ruby 的编译器中。你得到的值是正确的模 2**64。 (准确地说,是 107 * 2**64。)
  • @hack3r,可能与您安装的版本有关。你是怎么安装的?在什么平台上? here is a proof 它在 64 位 CentOS 上的所有 rubies 所有版本上都能正常运行

标签: ruby math multiplication


【解决方案1】:

这看起来像是 Ruby 对 Fixnum 类的乘法实现中的一个错误。查看源代码,numeric.c 中的 fixmul 函数中的以下几行(大量删减)可疑地跳出来:

...
2645:   long a, b;
...
2663:   c = a * b;
2664:   r = LONG2FIX(c);
...
2667:   if (FIX2LONG(r) != c || c/a != b) {
2668:       r = rb_big_mul(rb_int2big(a), rb_int2big(b));
...

如果我没看错的话,那是来自执行 Fixnum 乘法的代码,以及应该进行溢出检测以确定结果是否实际上应该重新计算为 Bignum 的代码位.问题在于它是糟糕的 C 代码:ab 具有 long 类型,并且如果 a * b 溢出会根据 C 标准产生未定义的行为。一个好的编译器可以通过假设在正确的 C 程序中永远不会发生溢出来利用这一点,并且基于该逻辑可以优化溢出检查 c / a != b

因此,如果您的 Ruby 版本是使用相当新的编译器编译的(最新版本的 Clang 就是一个很好的例子),这可能是造成这种情况的原因。

无论如何,我认为值得报告错误:即使这不是您所看到的原因,上面的 C 代码也是可疑的。


编辑:Daniel Fischer 在 cmets 中指出有一个 volatile long c 声明。理论上,这应该可以防止溢出检查被优化掉。因此,如果问题的原因,那么这将使它成为编译器错误,而不是 Ruby 代码中的错误。

【讨论】:

  • 你是对的 - 我会提交错误报告。此外,这似乎只是我的机器上的问题。我尝试了一堆在线口译员,似乎在那里工作得很好。这让我相信这可能真的是一个错误
  • 它也可能是一个编译器错误,或者表明 Ruby 没有为您的机器使用正确的编译器标志。如果不使用 gcc 的“-fwrapv”选项,Python 过去也存在类似问题。
  • 声明volatile long c;会禁止取消c / a != b检查,所以如果取消了,就是C编译器的bug了。
  • @DanielFischer:我看到他们在惠普机器上使用volatile 声明(用于ab)。
  • 啊,哎呀。他们已经在使用volatile long c。好点子。也许这一个编译器错误。
猜你喜欢
  • 1970-01-01
  • 2018-07-10
  • 1970-01-01
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-21
  • 2015-08-16
相关资源
最近更新 更多