Ruby sum stdin整数

daw*_*awg 3 ruby stdin

我有:

$ ruby -v
ruby 2.0.0p648 (2015-12-16 revision 53162) [universal.x86_64-darwin16]
Run Code Online (Sandbox Code Playgroud)

假设你有一个整数序列,1..nruby新手会像这样对序列求和:

$ ruby -e 's=0
     for i in 1..500000
        s+=i
     end
     puts s'
125000250000
Run Code Online (Sandbox Code Playgroud)

现在假设我有相同的序列来自stdin:

$ seq 1 500000 | ruby -lne 'BEGIN{s=0}
                            s+=$_.to_i
                            END{puts s} '   
125000250000
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.

现在将终端值从500,000增加到5,000,000:

$ ruby -e 's=0
         for i in 1..5000000
            s+=i
         end
         puts s'
12500002500000   <=== CORRECT

$ seq 1 5000000 | ruby -lne 'BEGIN{s=0}
                             s+=$_.to_i
                             END{puts s} '
500009500025     <=== WRONG!
Run Code Online (Sandbox Code Playgroud)

它产生不同的总和.

awk并且perl两者都以相同的顺序产生正确的结果:

$ seq 1 5000000 | awk '{s+=$1} END{print s}'
12500002500000
$ seq 1 5000000 | perl -nle '$s+=$_; END{print $s}'
12500002500000
Run Code Online (Sandbox Code Playgroud)

为什么红宝石会产生不正确的金额?我不认为它是溢出的,awk并且perl在相同的输入上正常工作.


结论:

谢谢David Aldridge对此进行诊断.

  1. OS X和BSD seq转换为1,000,000的浮点输出,而GNU seq支持任意精度整数.OS X seq作为大于1,000,000的整数来源是无用的.OS X上的示例:

    $ seq  999999 1000002
    999999
    1e+06
    1e+06
    1e+06
    
    Run Code Online (Sandbox Code Playgroud)
  2. ruby方法以.to_i静默方式将部分字符串转换为整数,在这种情况下就是"bug".例:

    irb(main):002:0> '5e+06'.to_i
    #=> 5
    
    Run Code Online (Sandbox Code Playgroud)
  3. 脚本中的"正确"行是用于$_.to_f.to_i使用浮点数,还是Integer($_)用于不让脚本无声地失败.awk并将perl5e + 06解析为浮点数,ruby并不隐式:

    $ echo '5e+06' | awk '{print $1+0}'
    5000000
    $ echo '5e+06' | ruby -lne 'print $_.to_i+0'
    5
    
    Run Code Online (Sandbox Code Playgroud)
  4. 感谢StefanSchüßler打开关于行为的Ruby 功能请求.to_i.

Dav*_*dge 5

我不确定这是100%的答案,但我注意到:

seq 500000 500001 | ruby -lne 'BEGIN{}
                             puts $_
                             END{} '
500000
500001
Run Code Online (Sandbox Code Playgroud)

......但......

seq 5000000 5000001 | ruby -lne 'BEGIN{}
                             puts $_
                             END{} '
5e+06
5e+06
Run Code Online (Sandbox Code Playgroud)

...所以#to_i将值转换为整数所采用的"放松"方法仍然有效......

seq 5000000 5000001 | ruby -lne 'BEGIN{}
                             puts $_.to_i
                             END{} '
5
5
Run Code Online (Sandbox Code Playgroud)

...但更严格的#to_int不会

seq 5000000 5000001 | ruby -lne 'BEGIN{}
                             puts $_.to_int
                             END{} '
-e:2:in `<main>': undefined method `to_int' for "5e+06":String (NoMethodError)
Run Code Online (Sandbox Code Playgroud)

编辑:我也注意到:

seq 5000000 5000001

5e+06
5e+06
Run Code Online (Sandbox Code Playgroud)

因此必须将-f标志传递给seq以获取整数格式.

再次编辑:

最终答案:

seq -f %f 1 5000000 | ruby -lne 'BEGIN{s=0}
                                  s+=$_.to_i
                                 END{puts s} '

12500002500000
Run Code Online (Sandbox Code Playgroud)