目次
Ruby で 小数点以下第n桁で切り捨てる方法をメモしておきます。
0.81235 などのリテラル (Float) には floor という関数があり、 小数点以下の切り捨てが可能ですが、 小数点以下の桁を残して切り捨てることはできません。
環境
- Ruby 2.2.3p173
- OS: Ubuntu 15.04
解決法
調べたり考えたりした結果、 3つの方法が見つかりました。
BigDecimal を使う
BigDecimal なら floor
という関数に小数点以下の桁数を指定して切り捨てることができます。 BigDecimal を作るときは 引数を String にする必要があります。
1 2 3 4 5 |
require 'bigdecimal' def dec_floor(num, n) BigDecimal.new(num.to_s).floor(n).to_f end |
桁を操作する
小数点以下第n桁まで欲しければ、 ( 10^n ) 倍 してから ( 10^n ) で割ればいいです。
1 2 3 4 5 6 7 8 9 |
def dec_floor(num, n) 1.upto(n) do num *= 10 end num.floor 1.upto(n) do num /= 10 end end |
半分引いて四捨五入する
0.1 以上 0.2 未満 の数を 0.05 以上 0.15 未満 の数に投影して四捨五入すれば 小数点以下2桁を残して切り捨てたことになります。
1 2 3 |
def dec_floor(num, n) (num - 0.5 * 0.1 ** n).round(n) end |
これは num
が正の数の場合には正しい値を返します。 num - 0.5 * 0.1 ** n
が負の数になる場合は round
が 四捨五入にならないことがあるからです。 (-0.5).round
は - 1
になります。
もし、 0 以上 で正しい答えを返すようにするなら次のような関数にします。
1 2 3 |
def dec_floor(num, n) (num + 0.5 * 0.1 ** n).round(n) - 0.1 ** n end |
しかしこれはこれで、 最終結果が丸められずに 0.009999999999999998 (dec_floor(0.01, 2)
) のようになることがあります。
失敗する方法
sprintf
で文字列に変換する
sprintf
で文字列に変換すると 自動的に四捨五入されてしまいます。
1 |
sprintf("%.3f", 1.66666666666) # => 1.667 |
四捨五入されるので 上で紹介したように 半分引いて sprintf
を使えば目的の文字列が取得できます。 数値として変換するわけではないのでおすすめはしません。