[PHP-users 35361] Re: 小数点以下を含まない ceil()の実行結果

Masanori Nakashima m_nakashima @ md-systems.net
2010年 9月 6日 (月) 17:47:39 JST


中島と申します

> $a=2*10770*1.35;
> echo $a;        // 29079
> echo ceil($a);  // 29080
この浮動小数点における切り上げ・切り捨ての問題は
変数型キャストの問題、PHPだけの問題ではないとは思います。

浮動小数点数の精度に関する問題については下記ページに記載があります。
http://jp2.php.net/manual/ja/language.types.float.php

1行目部分で$aはfloat型に暗黙的にキャストされるので精度を保てなく
なっているのだと思います。

実装1)精度を維持して浮動小数点計算をおこなうにはPHPの場合は、
bcmul関数などで乗算するのが一番良いのだろうと思います。

BC Math関数
http://jp2.php.net/manual/ja/ref.bc.php

ご指定のような式の結果を出力するのでしたら下記のように小数以下2桁
に揃えて計算するということであれば下記のようにすれば正確にでます。

$a = bcmul( 2, bcmul( 10770, 1.35, 2 ), 2 );
echo $a;        // 29079.00
echo ceil($a);  // 29079

実装2)整数に揃えてから計算して小数に戻す
Cなんかで浮動小数点乗算するときのセオリーですね。
良く使います。最初に小数点以下の精度がある程度分かっている事が前提ですが

$a= 2 * 10770 * 1.35 * 100;
echo ($a/100);        // 29079.00
echo ceil($a/100);  // 29079

実装3)一度文字列型にキャストしてから計算する
私の場合結構つかったりしています
私の環境と要件で問題なく動作しているというだけで、
汎用的にはお勧めできませんが…

$a = (string) (2*10770*1.35);
echo $a;        // 29079
echo ceil($a);  // 29079

これだと処理の順序として、2*10770*1.35の結果を一度文字列にキャスト
して内部値が文字列になってfloatの範囲で丸められます。
その後ceilした時に文字列を再度float型にキャストしますので、計算結果と
表示文字列の整合性がとれるということですが…
かなり強引なやり方なのは分かっているのですが簡便なのでつい利用してしまいます。
これだとこのような問題があるなどのご指摘はぜひ頂きたいです。

□■◇ Masanori Nakashima 
■□■ MDS co.,Ltd. 
□■□ mail : m_nakashima @ md-systems.net
■□■ URL  : http://www.md-systems.net/
□■◇ オープンソースMDS Tools開発チーム
■□■ URL  : http://www.mds-tools.com/
□■□ Tel  : 03-5950-0525
■□■ Fax   : 03-5950-0526
□■□ Zip   : 170-0013
■□■ 311, 25 Sankyo-Blg 1-48-10 Toshimaku Tokyo Jp



PHP-users メーリングリストの案内