Rubyで計算がずれてしまう理由と対策を教えてください。


小数点を含む割り算の計算結果が思い通りになりません。

例えば、 `670 ÷ 0.67` としたとき `1000` になってほしいのですが、 `999.9999999999999` となってしまいます。

[code]

    $ ruby -v
    ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin18]
    
    $ irb
    irb(main):001:0> 670 / 0.67
    => 999.9999999999999
    
[/code]

Node.jsでも同じでした。

[code]

    $ node
    Welcome to Node.js v12.1.0.
    Type ".help" for more information.
    > 670 / 0.67
    999.9999999999999
    
[/code]

割り切れる数値だけ扱うプログラムなのですがうまく作れずに困っています。  
こんな簡単な計算で躓くと思っていなかったので、小数を扱うときに気をつけることがいろいろありそうで不安です。

なぜこのような計算ができないのか、またどのように処理を書くのが正しいのか教えていただけると幸いです。

環境はmacOSです。




Ruby(やJavaScript)に限らず、小数を表すデフォルトのデータ型として2進浮動小数点数を使用している言語(C#, Java,
Swift等々、よく見る主要な言語の殆どと言っていい)に共通して見られる現象です。

簡単に言うと **2進浮動小数点数では`0.67`のような値を正確に表現することができないから** と言う事になります。

より詳しい解説については、ここ日本語版スタックオーバーフローでも「計算
誤差」辺りで検索すると、いろいろヒットしますので、興味があればご自身で調べて見てください。例えば、私がJavaScriptでの同様の問題について書いたものは[こちら](https://ja.stackoverflow.com/a/49380/13972)。

**またどのように処理を書くのが正しいのか** については、目的によります。

ざっくり挙げていくと

  * 内部的にも10進演算を行うライブラリを使用する

Rubyなら`BigDecimal`

  * 四捨五入をうまくやって誤魔化す

Excelは実はこれ

  * コンピュータでの演算はこんなものだと開き直る

科学技術計算では演算誤差が入るのは当たり前

macOS上のirbで`BigDecimal`を使った例はこちら。

[code]

    irb(main):001:0> require 'bigdecimal'
    => true
    irb(main):002:0> a = BigDecimal("670")
    => #<BigDecimal:7fad04015f78,'0.67E3',9(18)>
    irb(main):003:0> b = BigDecimal("0.67")
    => #<BigDecimal:7fad0400dd00,'0.67E0',9(18)>
    irb(main):004:0> print (a/b).to_s('F')
    1000.0=> nil
    
[/code]




Ruby(やJavaScript)に限らず、小数を表すデフォルトのデータ型として2進浮動小数点数を使用している言語(C#, Java,
Swift等々、よく見る主要な言語の殆どと言っていい)に共通して見られる現象です。

簡単に言うと **2進浮動小数点数では`0.67`のような値を正確に表現することができないから** と言う事になります。

より詳しい解説については、ここ日本語版スタックオーバーフローでも「計算
誤差」辺りで検索すると、いろいろヒットしますので、興味があればご自身で調べて見てください。例えば、私がJavaScriptでの同様の問題について書いたものは[こちら](https://ja.stackoverflow.com/a/49380/13972)。

**またどのように処理を書くのが正しいのか** については、目的によります。

ざっくり挙げていくと

  * 内部的にも10進演算を行うライブラリを使用する

Rubyなら`BigDecimal`

  * 四捨五入をうまくやって誤魔化す

Excelは実はこれ

  * コンピュータでの演算はこんなものだと開き直る

科学技術計算では演算誤差が入るのは当たり前

macOS上のirbで`BigDecimal`を使った例はこちら。

[code]

    irb(main):001:0> require 'bigdecimal'
    => true
    irb(main):002:0> a = BigDecimal("670")
    => #<BigDecimal:7fad04015f78,'0.67E3',9(18)>
    irb(main):003:0> b = BigDecimal("0.67")
    => #<BigDecimal:7fad0400dd00,'0.67E0',9(18)>
    irb(main):004:0> print (a/b).to_s('F')
    1000.0=> nil
    
[/code]

