2009年1月27日

ヤフオク!

awkでの数値演算注意

  • このエントリーをはてなブックマークに追加
オークション事業部 プラットフォーム技術 後藤です。

私たちのチームはauctionリスト、サーチ、課金などのサービスの運用業務を
行っておりますが、今回は課金の検算などでよく利用するawkでの数値演算につ
いてレポートします。

消費税算出など小数点付きのものを四則演算する場合、awk(表示形式のデフォル
ト書式)を用いて生成する場合、以下ような現象が出ますので注意が必要です。
「7けた以上で科学表記になる場合がある」
「小数点を含めたけた数が7けた以上になると、誤差が出る場合がある」
などです。

以下の理屈が分かっていれば、なにも不思議はないのですが、結構誤差が出るパ
ターンも多いので、利用者の意識が必要なのだなーと思っています。

[awkの説明]
print 文を数値を出力するために使った場合、awkは内部で数値を (その数値を表す)文字列に変換し、
その文字列を出力する。 awkはこの変換動作のためにsprintf関数を使用する。
組込み変数のOFMTは、出力をおこなうときにprintがsprintf を使って数値を文字列に変換するための
書式のデフォルト設定を保持している。
OFMTのデフォルトの値は"%.6g"である。
CONVFMTのデフォルトの値は"%.6g"であり、これは値を最低6文字あるとして変換する。
awkではすべての数値は倍精度の浮動小数点数である。
最近のマシンの大部分では倍精度実数は十進数で16から17けたの精度がある。
Controlling Numeric Output with print抜粋


[sprintfの形式]
"%.6g" 科学表記か浮動小数点表記の、いずれか出力するキャラクタ数が少なくなる方で出力する。
"%.6e" 数値を科学(または指数)形式で出力する。
"%.6f" 浮動小数点表記で小数点以下のけた数表示指定する。


[表記説明]
科学表記は10のべき乗で表す
  echo ""|awk '{ OFMT = "%.6e"} {print 100000000000}'
結果:1.000000e+11
(浮動小数点表記はXXXXX.XXの形で表す)
  echo ""|awk '{ OFMT = "%.6f"} {print 100000000000}'
結果:100000000000.000000

[浮動小数点数型と誤差]
・表示形式による表記違い(計算結果が7けた以上の場合)
OFMTのデフォルトの値は"%.6g"ですので、科学表記か浮動小数点表記いずれかの出力になります。
 表記はキャラクタ数が少なくなる方で出力されます。
 echo "" | awk '{ OFMT = "%.6g"} {print 1815755+0.95  }'
結果:1.81576e+06

・丸め誤差(近い整数に丸められる)
OFMTのデフォルトの値は"%.6g"ですので、小数点を含めたけた数が7けた以上
 になると、誤差が出る場合があります。
 echo "" | awk '{ OFMT = "%.6e"} {print 223359*0.05  }'
結果:11168
(本来「11167.95」からの切り捨てデータとして「11167」を欲してますが、 近い整数に丸められています)

・表示形式による情報落ち(小さな値)
OFMTのデフォルトの値は"%.6g"ですので、6けた以下の情報は失われます。
 echo "" | awk '{ OFMT = "%.6g"} {print 1/3 }'
結果:0.333333
(0.0000003333のデータは失われます)

・丸め誤差(小さな値の情報落ち)
 絶対値が大きな値と小さな値とを加えた場合、小さい方の数値がもつ情報は失われます。
 echo "" | awk '{ OFMT = "%.6g"} {print 77777.7 + 1.23456}' 
結果:77778.9
(0.03456のデータは失われます)


Yahoo! JAPANでは情報技術を駆使して人々や社会の課題を一緒に解決していける方を募集しています。詳しくは採用情報をご覧ください。

  • このエントリーをはてなブックマークに追加