2014/06/04
データいろいろ
 >  PostgreSQL と R 四捨五入と銀行丸め
昨日の最後に書いた四捨五入の件。実行環境は 2014/04/17 のとおり。前提として、端数処理(丸め)は四捨五入が唯一の方法ではなく、銀行丸め(最近接偶数への丸め)をはじめ多くの考え方と方法がある。詳細は、例えば下記を参照。

■ Wikipedia : 端数処理
http://ja.wikipedia.org/wiki/%E7%AB%AF%E6%95%B0%E5%87%A6%E7%90%86

この「方法」の違いだけでなく、PC では「実装」と「精度」の問題が加わる。実装も OS からライブラリ・言語・ソフトウェアまでいろいろ。以下の「変な結果の例」は、特定の言語の不備とかではなく PC で10進数を扱う以上不可避なもの。しかし気を付ける必要はある。

↓ 昨日の再現。R の sprintf 関数を使ったら、銀行丸めと四捨五入(赤線部)が混在した。関数のドキュメント には sprintf is a wrapper for the system sprintf C-library function. とあるが、丸めの方法は不明。


■ R Documentation : sprintf {base} Use C-style String Formatting Commands
http://stat.ethz.ch/R-manual/R-devel/library/base/html/sprintf.html

R には別途 round と signif という関数があり、ドキュメントによれば go to the even digit つまり銀行丸め。ただし OS や10進数 ⇔ 2進数変換の問題があり(例えば 0.15 は2進数でぴったり表せない)、「0.15 を丸めると 0.1 にも 0.2 にもなり得る」と明記されている。今日の問題が端的に分かるので、当該部分を引用。

■ R Documentation : Rounding of Numbers
http://stat.ethz.ch/R-manual/R-devel/library/base/html/Round.html
Note that for rounding off a 5, the IEC 60559 standard is expected to be used, ‘go to the even digit’. Therefore round(0.5) is 0 and round(-1.5) is -2. However, this is dependent on OS services and on representation error (since e.g. 0.15 is not represented exactly, the rounding rule applies to the represented number and not to the printed number, and so round(0.15, 1) could be either 0.1 or 0.2).

↓ 先ほど sprintf に渡した五つの値を signif で丸めたら、ドキュメントどおりすべて銀行丸めになった。一方 round では一つだけ切り上げが発生。値を2進数にした際、丸める対象の数が 5 よりわずかに増えてしまったのだろうか。



このように一つの実行環境・言語でも違う結果になり得るのだから、複数の環境・言語・バージョンでは尚更。下記にある様々な例と原因の考察が参考になる。

■ IT-usenet.org : [ruby-list:48330] 1.25を %10.1f で出力したときバージョンにより表示に違いがでるのは何故でしょうか。
http://en.it-usenet.org/thread/14709/7454/

↓ 先ほど R で試した五つの値を PostgreSQL の to_char と round で丸めると、すべて普通の四捨五入になった。
WITH a AS (
VALUES (98765 :: numeric)
, (9876.5)
, (987.65)
, (98.765)
, (9.8765)
), b AS (
VALUES (4) -- 指数表記での有効桁数
)
SELECT a.column1
, translate(to_char(a.column1
, concat('.', repeat('0', b.column1 - 1), 'EEEE'))
, 'e ', 'E')
, round(a.column1, (4 - ceil(log(a.column1))) :: int)
FROM a, b ;


to_char のドキュメント(下記一つ目のリンク)には、丸めの方法について特に記載がない。round のドキュメント(二つ目のリンク)には簡潔に「四捨五入」とある。

■ PostgreSQL 9.3.2文書 : データ型書式設定関数
http://www.postgresql.jp/document/9.3/html/functions-formatting.html

■ PostgreSQL 9.3.2文書 : 算術関数と演算子
http://www.postgresql.jp/document/9.3/html/functions-math.html

余談だが、上の 98765 → 98770 のように整数部の丸めを round 関数で行える。第二引数をマイナスにするだけ。下が簡単な例。ドキュメントにないのでメモした。


下記 ML では、少し古いバージョン 8.2 と 8.3 で round の結果が違うという投稿と、OS の rint 関数を使っているからという返答。同じ値を自分の環境で試したら普通の四捨五入になった。

■ PostgreSQL - general (メーリングリスト) : Rounding incompatibility
http://postgresql.1045698.n5.nabble.com/Rounding-incompatibility-td1920121.html
<< Excel の表を Acrobat X Pro で段組み
PostgreSQL と R それぞれの指数表… >>
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。