[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 メーリングリストの案内