[pgsql-jp: 37166] Re: 消費税計算で失敗しました。

Kawasaki Yusuke u-suke @ kawa.net
2006年 6月 6日 (火) 12:17:34 JST


川崎です。こんにちは。
お金の処理を離れて、浮動小数点処理に関する興味としては、

--------------------------------
SELECT (1+tax_rate) FROM test;                      -- 1.05000000074506
SELECT (1::double precision+tax_rate) FROM test;    -- 1.05000000074506
SELECT tax_rate::double precision FROM test;        -- 0.0500000007450581
SELECT (1::real+tax_rate) FROM test;                -- 1.05
SELECT tax_rate::real FROM test;                    -- 0.05
--------------------------------

(1+tax_rate) はdoubleで計算されるため、tax_rate に内包されていた
(でも表面化していなかった)誤差が表面化したわけですね。

--------------------------------
select (1::integer+0.5::real);                      -- 1.5
select (1::double precision+0.5::real);             -- 1.5
select (0.5::real::double precision);               -- 0.5
--------------------------------

test テーブルに格納せずに、定数で計算した場合は誤差が表面化しないのは、
原因がよく分かりません。

また、小数点以下の0になる桁を自動で省いてくれる処理は printf の "%g" 相当でしょうか。
http://www.linux.or.jp/JM/html/LDP_man-pages/man3/printf.3.html
によると「精度が指定されない場合は、6桁とみなされる。」そうなので少し違うのか。

--------------------------------
#include <stdio.h>
int main () {
    float tax_rate = 0.05;
    printf( "%f\n", (float)1+tax_rate );        // 1.050000
    printf( "%f\n", (double)1+tax_rate );       // 1.050000
    printf( "%g\n", (float)1+tax_rate );        // 1.05
    printf( "%g\n", (double)1+tax_rate );       // 1.05
    printf( "%.15f\n", (float)1+tax_rate );     // 1.04999995231628
    printf( "%.15f\n", (double)1+tax_rate );    // 1.05000000074506
}
--------------------------------

DB 内部はともかく、C でお金を扱う場合は、どうしているんでしょう?
・・・話題が脱線しすぎました。


On 6/6/06, Koichi Hyodo <sio-0 @ rh.to> wrote:
> > > お金を扱うときは double precision でないとダメですね。
> > || numeric型は、最大1000桁の精度で数値を格納でき、正確な計算を行えます。
> > とのことですので、できることなら numeric 型が良さそうですが、
> > double precision でも、100兆〜銭の位まで使えるならほぼ実用的か。
> そういうどんぶり勘定を示すのはどうかなと思いますが…。
> 浮動小数点型は、10進小数ではなく2進小数ですから
> 10進数と変換する毎に最下位桁に割り切れない誤差ができます。

そうですね、僕もお金は numeric がいいと思います。


> > あるいは、安易にパーセンテージの値を100倍してinteger の 5として記録しておくと
> > 将来的な税率見直しで、もしも例えば7.5%とかになると困りますね。
> > あるいは1000倍して、銭の下で1000分の1円の厘単位のintegerで計算するか。
> integerだと今度は桁数が足らないことがあるかも。

本当だ、千分率の税率でなくて、1000倍した金額を integer で表すと、
数百万円単位でスグに桁あふれしてしまいますね。ECサイトでも使えない。
    select 2147483647 + 1;
    ERROR:  integer out of range
また、処理を間違えて1000倍の請求書を送ったり入金をしてしまう可能性が
大変危険なので、自前で固定小数点処理を行うよりは、多少計算処理に
時間がかかたとしても、DBにお願いした方が安全ですね。失礼しました。

ではでは。

-- 
Kawasaki Yusuke <u-suke @ kawa.net> http://www.kawa.net/



pgsql-jp メーリングリストの案内