整数化の問題 (オレオレ int を作る)
Perlで整数判定がしたい - みずぴー日記 や「切り捨て」に int() は使うべからず - にぽたん研究所に int() 関数がダメということが書かれていますが、既に7021 != 7021 (Part 3) - ときどきの雑記帖 リターンズ 2006年12月に既に議論されています。
元の数値(70.21*100)に適当なεを加えてやってから整数化するか、整数化する 前とした後の数値を比較してある範囲に入っていたら整数化した数値に 1加えて やるかすればいいのではないでしょうか?
なので、オレオレ int() 関数を作ってみます。
#! /usr/local/bin/nawk -f
# ore_int.awk
BEGIN {
print "int(125 ^ (1 / 3)) = " \
int(125 ^ (1 / 3));
print "ore_int(125 ^ (1 / 3)) = " \
ore_int(125 ^ (1 / 3));
print "int(70.21 * 100) = " \
int(70.21 * 100);
print "ore_int(70.21 * 100) = " \
ore_int(70.21 * 100);
print "int(0.5005 * 10000) = " \
int(0.5005 * 10000);
print "ore_int(0.5005 * 10000) = " \
ore_int(0.5005 * 10000);
print "int(1.0000000000000000000000000001) = " \
int(1.0000000000000000000000000001);
print "ore_int(1.0000000000000000000000000001) = " \
ore_int(1.0000000000000000000000000001);
print "int(0.9999999999999999999999999999) = " \
int(0.9999999999999999999999999999);
print "ore_int(0.9999999999999999999999999999) = " \
ore_int(0.9999999999999999999999999999);
print "int(-1.0000000000000000000000000001) = " \
int(-1.0000000000000000000000000001);
print "ore_int(-1.0000000000000000000000000001) = " \
ore_int(-1.0000000000000000000000000001);
print "int(-0.9999999999999999999999999999) = " \
int(-0.9999999999999999999999999999);
print "ore_int(-0.9999999999999999999999999999) = " \
ore_int(-0.9999999999999999999999999999);
}
# 2 進数表記できない場合にある程度丸めた整数化を行います (オレオレ int)
# in: 数値 num
# out: num の整数化されたもの
function ore_int(num, str, period) {
str = sprintf("%100.99f\n", num) "";
##print str;
if (abs(sprintf("%100.99f", str - int(num) - get_e())) < 0.000000000001) {
return int(num) + 1;
} else {
return int(num);
}
}
# 絶対値を返す
# in: 数値 num
# out: 数値 num の絶対値
function abs(num) {
if (num >= 0) {
return num;
} else {
return -num;
}
}
function get_e() {
return 0.999999999999999;
}
例として、いくつか標準 int() 関数で問題になりそうなものを集めてみました。 計算結果は以下のようになります。
$ nawk -f ore_int.awk int(125 ^ (1 / 3)) = 4 ore_int(125 ^ (1 / 3)) = 5 int(70.21 * 100) = 7020 ore_int(70.21 * 100) = 7021 int(0.5005 * 10000) = 5004 ore_int(0.5005 * 10000) = 5005 int(1.0000000000000000000000000001) = 1 ore_int(1.0000000000000000000000000001) = 1 int(0.9999999999999999999999999999) = 1 ore_int(0.9999999999999999999999999999) = 1 int(-1.0000000000000000000000000001) = -1 ore_int(-1.0000000000000000000000000001) = -1 int(-0.9999999999999999999999999999) = -1 ore_int(-0.9999999999999999999999999999) = -1
ここの 0.9999999999999999999999999999 は 1 として扱われているわけですが、どのへんから 1 として扱われているのかを簡単なスクリプトで見てみます。
#! /usr/local/bin/nawk -f
# int_test.awk
BEGIN {
a = "0.";
for (i = 1; i <= 100; i++) {
a = a "9";
b = a + 0;
printf("str = %s\n", a);
printf("num = %51.50f\n", b);
}
}
実行してみます。
$ nawk -f int_test.awk str = 0.9 num = 0.90000000000000002220446049250313080847263336181641 str = 0.99 num = 0.98999999999999999111821580299874767661094665527344 str = 0.999 num = 0.99899999999999999911182158029987476766109466552734 str = 0.9999 num = 0.99990000000000001101341240428155288100242614746094 str = 0.99999 num = 0.99999000000000004551026222543441690504550933837891 str = 0.999999 num = 0.99999899999999997124433548378874547779560089111328 str = 0.9999999 num = 0.99999990000000005263558477963670156896114349365234 str = 0.99999999 num = 0.99999998999999994975240724670584313571453094482422 str = 0.999999999 num = 0.99999999900000002828193146342528052628040313720703 str = 0.9999999999 num = 0.99999999989999999172596290009096264839172363281250 str = 0.99999999999 num = 0.99999999998999999917259629000909626483917236328125 str = 0.999999999999 num = 0.99999999999900002212172012150404043495655059814453 str = 0.9999999999999 num = 0.99999999999989996890548127339570783078670501708984 str = 0.99999999999999 num = 0.99999999999999000799277837359113618731498718261719 str = 0.999999999999999 num = 0.99999999999999900079927783735911361873149871826172 str = 0.9999999999999999 num = 0.99999999999999988897769753748434595763683319091797 str = 0.99999999999999999 num = 1.00000000000000000000000000000000000000000000000000 <snip>
つまり 0.99999999999999999 はどうやっても 1 に見なされてしまうため、xgawk の MFPR 機能を使わないとこのあたりが限界のようです。 前述の ore_int() 関数はもう少しゆるめに作ってありますので、うまく機能しない場合は調整してください。




