鈴木商店の嶋崎です。こんにちは。
PHPExcelを使っていておかしな現象に遭遇したので報告と共有です。
現象としてはタイトルどおり、PHPExcelでテンプレートExcelファイルを読み込むとroundが返す値がおかしくなると言うものです。
例えば集計して割合表示とかやりますよね?
それではやってみましょう。
環境
PHP5.6.31
下準備
Githubから最新リリースをダウンロード
https://github.com/PHPOffice/PHPExcel/releases
執筆時、1.8.1でした。
実証
- サンプルコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php //パスは必要に応じて読み替えてください require_once dirname(__FILE__) . '/../Classes/PHPExcel.php'; /** * パーセンテージ表示 */ function floatCalc(){ echo round(8/9 * 100, 1); echo "<br />"; } //before floatCalc(); $objPHPExcel = new PHPExcel(); //after floatCalc(); |
結果
88.9
88.90000000000001
なんということでしょう。丸められていません。
原因
どういうことかというと以下をご覧ください。
PHPExcel_Calculationのコンストラクタです。
1 2 3 4 5 6 |
$setPrecision = (PHP_INT_SIZE == 4) ? 14 : 16; $this->_savedPrecision = ini_get('precision'); if ($this->_savedPrecision < $setPrecision) { ini_set('precision',$setPrecision); } |
PHP_INT_SIZE
が「4」を返さない。つまり64bit環境です。ini_get('precision')
ですが、デフォルトは「14」です。
つまり、64bit環境で、precision
を変更していない状態でPHPExcelを使うと、
強制的にprecision
が「16」に変更されます。
precision
が「14」よりも大きい状態でround
を使うとちょいちょい期待しない結果になるのは有名な話ですね。
どうすればいいか?
開発バージョンを使う
実はこの問題PHPExcel側も認識していて、1.8ブランチの最新ソースではprecision
の変更は行われていません。
ただ、テストが済んでないらしく、最新リリースには取り込まれていません。また、取り込まれる予定もなさそうです。
https://github.com/PHPOffice/PHPExcel/issues/237
使用は自己責任で。
PHPExcelの初期化を遅らせる
PHPExcelを使わない限り、precision
は変更されません。
先にround
してからPHPExcelを使えばこの問題は回避できます。
とは言え、処理の呼び出し順で結果が変わるのは開発者に優しくありません。
PHPExcelの使用箇所が少なければこの問題で回避するのも手です。
roundを使わない
代わりにsprintf
を使います。
sprintf
はprecision
の影響を受けないため、PHPExcelが何しても大丈夫です。
おそらくはこれが順当な対策ではないかと思います。
ちなみに、今回はこの方法を採用しました。
他には計算をExcel側でやらせる、力技ではini_setをコメントアウトしてしまう、といった案も考えられなくは無いです。
同じような問題に遭遇した方が「PHPExcel round」でググってこのページが解決になれば幸いです。