Cogito Ergo Sum.

我思う故に我あり

銀行型丸め 2

 Haskellで「小数点以下、四捨五入」をしたくて標準ライブラリPreludeに含まれている「round」関数を使ってみたところ、どうも結果がおかしい。調べてみると、どうやら「round」は「銀行型丸め」(小数部分が「0.5」ピッタリの場合、偶数に丸める)を行う関数のようだ。

Prelude> map round [-5.5 .. 5.5]
[-6,-4,-4,-2,-2,0,0,2,2,4,4,6]

 僕はこの「銀行型丸め」で昔ハマったことがある。すっかり忘れていたが、このブログにも書いていた(「2010年07月18日」だそうだ…。遠い昔じゃのう…)。

 そもそも「小数点以下、四捨五入」って、小数部分が「0.5以上の場合」に切り上げるのか、それとも「0.5を超えた場合」に切り上げるのか、数学的に定義されているというより、文化圏によって異なる「その文化での常識」みたいなもののような気がするし(「1から10まで」というのも、「1」や「10」を含むのか含まないのかは文化圏によって異なるように思う)、コンピューターで小数を扱う場合常に誤差の問題が付きまとうから意外とヤッカイなのに、その上こういう丸め方があるとは露とも知らず、大いに戸惑ったような記憶がある。

 ついでにHaskellの他の関数についても試してみたところ、「切り上げ」を行う「ceiling」関数は、正の数も負の数も常に大きい方の整数を、「切り下げ」を行う「floor」関数は、正の数も負の数も常に小さい方の整数を返すようなのだが、

Prelude> map ceiling [-5.5 .. 5.5]
[-5,-4,-3,-2,-1,0,1,2,3,4,5,6]

Prelude> map floor [-5.5 .. 5.5]
[-6,-5,-4,-3,-2,-1,0,1,2,3,4,5]

「truncate」関数は、「0に近い方の整数」を返すようだ(正の数は「切り下げ」、負の数は「切り上げ」になる)。

Prelude> map truncate [-5.5 .. 5.5]
[-5,-4,-3,-2,-1,0,0,1,2,3,4,5]

 ちなみに、元の数列はこんな感じ。

Prelude> map id [-5.5 .. 5.5]
[-5.5,-4.5,-3.5,-2.5,-1.5,-0.5,0.5,1.5,2.5,3.5,4.5,5.5]

 これまで「『id』なんて何のために存在する関数なんだろう?」と思っていたんだけど、こんな風に使うのかな?