小数点以下の桁数での四捨五入

例えば Python などには小数点以下の桁数での四捨五入の関数として round() 関数が用意されていますが、awk には round() 関数がありません。 どういう条件で四捨五入する関数が便利なのかは意見が分かれるところだと思いますが、Python のような小数点以下の桁数で四捨五入する関数を作ってみます。

#! /usr/local/bin/nawk -f
# round.awk
# usage: nawk -f round.awk

BEGIN {
    printf("%.18e\n", round(100, 0));
    printf("%.18e\n", round(100, 1));
    printf("%.18e\n", round(0, 0));
    printf("%.18e\n", round(0, 1));
    printf("%.18e\n", round(0.01, 0));
    printf("%.18e\n", round(0.01, 1));
    printf("%.18e\n", round(0.05, 0));
    printf("%.18e\n", round(0.05, 1));
    printf("%.18e\n", round(-100, 0));
    printf("%.18e\n", round(-100, 1));
    printf("%.18e\n", round(-0.01, 0));
    printf("%.18e\n", round(-0.01, 1));
    printf("%.18e\n", round(-0.05, 0));
    printf("%.18e\n", round(-0.05, 1));
    printf("%.18e\n", round(5.3e-25, 24));
}

# round():  小数点以下の桁数での四捨五入
#   in:     数値 num
#           小数点以下の桁数 digit_num
#   out:    小数点以下の桁数 digit_num で四捨五入した数値 num
function round(num, digit_num) {
    if (num >= 0) {
        return int(num * 10 ^ digit_num + 0.5) / (10 ^ digit_num);
    } else {
        return int(num * 10 ^ digit_num - 0.5) / (10 ^ digit_num);
    }
}

正か負かで四捨五入する方向が異なる点に注意してください。

実行してみましょう。

$ gawk -f round.awk
1.000000000000000000e+02
1.000000000000000000e+02
0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00
1.000000000000000056e-01
-1.000000000000000000e+02
-1.000000000000000000e+02
-0.000000000000000000e+00
-0.000000000000000000e+00
-0.000000000000000000e+00
-1.000000000000000056e-01
1.000000000000000107e-24

このような結果になります。

では、同様のものを Python で検証してみましょう。

#!/usr/bin/python

print  "%.18e" % round(100, 0)
print  "%.18e" % round(100, 1)
print  "%.18e" % round(0, 0)
print  "%.18e" % round(0, 1)
print  "%.18e" % round(0.01, 0)
print  "%.18e" % round(0.01, 1)
print  "%.18e" % round(0.05, 0)
print  "%.18e" % round(0.05, 1)
print  "%.18e" % round(-100, 0)
print  "%.18e" % round(-100, 1)
print  "%.18e" % round(-0.01, 0)
print  "%.18e" % round(-0.01, 1)
print  "%.18e" % round(-0.05, 0)
print  "%.18e" % round(-0.05, 1)
print  "%.18e" % round(5.3e-25, 24)

これを実行してみます。

$ python round.py
1.000000000000000000e+02
1.000000000000000000e+02
0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00
0.000000000000000000e+00
1.000000000000000056e-01
-1.000000000000000000e+02
-1.000000000000000000e+02
-0.000000000000000000e+00
-0.000000000000000000e+00
-0.000000000000000000e+00
-1.000000000000000056e-01
1.000000000000000107e-24

同じ値が出力されているようです。

bash, zsh のようなシェルを使っている場合には以下のようにして、実行結果を直接 diff コマンドに渡すことができます。

$ diff -u <(python round.py) <(gawk -f round.awk)

さて、共に結果の一部に端数が出力されているのですが、これらは数値が 2 進数で表記できないために端数となっています。 こうした端数を処理するのは悩ましいのですが、Perl は Math::Round - search.cpan.org のように nearest という便利なものがあるようです。

tag_nawk.png tag_nawk.png tag_nawk.png tag_nawk.png