質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

3回答

6184閲覧

浮動小数点数演算に伴う小数点切り捨て処理がうまく行きません

退会済みユーザー

退会済みユーザー

総合スコア0

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

0クリップ

投稿2016/10/12 06:30

編集2016/10/12 09:10

###前提・実現したいこと
とある会計システムを作成している過程で想定する結果と違う値になっている箇所を複数発見しました。
調査した結果 floor を用いたときに引数が整数値のタイミングで不具合があるように見受けられました。

よって以下のソースを用いて確認してみました。

回避する方法は何かないでしょうか?
それとも環境による不具合でしょうか?
一応2種類の環境で試したところ同様の結果でした。

###該当のソースコード

PHP

1for ($i = (float) 0; $i < 10.0; $i+=0.1) { 2 var_dump([$i , floor($i)]); 3}

###発生している問題・エラーメッセージ
$i が整数値のときを抜粋。

array (size=2) 0 => float 0 1 => float 0 … array (size=2) 0 => float 1 1 => float 0 … array (size=2) 0 => float 2 1 => float 2 … array (size=2) 0 => float 3 1 => float 3 … array (size=2) 0 => float 4 1 => float 4 … array (size=2) 0 => float 5 1 => float 4 … array (size=2) 0 => float 6 1 => float 5 … array (size=2) 0 => float 7 1 => float 6 … array (size=2) 0 => float 8 1 => float 7 … array (size=2) 0 => float 9 1 => float 8 … array (size=2) 0 => float 10 1 => float 9

###補足情報(言語/FW/ツール等のバージョンなど)
手持ちの環境が以下しかありませんでした。
PHP Version 5.6.24
PHP Version 5.5.30

###BC Math 関数を用いた方法
とりあえず検証用で用意したソースはBC Math 関数を用いて解決しました。

PHP

1for ($i = (float) 0; $i < 10.0; $i = bcadd($i, 0.1, 1)) { 2 var_dump([$i, floor($i)]); 3}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

一瞬何が言いたいのかわからなかったので、示されたソースを手元の環境で実行してみたらやっとわかりました。

PHP: 浮動小数点数 - Manual
http://php.net/manual/ja/language.types.float.php

より高い精度が必要な場合には、 任意精度数学関数または gmp 関数を代わりに使用してください

とのことです。
そもそも精度が保証されていないのですから、別の方法でやるしかありません。

PHP: BC Math 関数 - Manual
http://php.net/manual/ja/ref.bc.php
PHP: GMP 関数 - Manual
http://php.net/manual/ja/ref.gmp.php

しかし、記述は面倒になります。

簡易的な解決方法を紹介するブログ記事がありました:

PHPの少数演算における切り上げ切捨て問題 - PSI Labs
http://www.psi-net.co.jp/blog/?p=277

投稿2016/10/12 06:47

編集2016/10/12 06:54
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2016/10/12 06:57

解答ありがとうございます。 GMPインストールされていたのでこちらで解決策見出していこうと思います。
guest

0

これは、浮動小数点数演算にともなって、必然的に発生する誤差です。

PHPを含め、多くの言語で浮動小数点数はIEEE 754という形式で表されていますが、これは2の累乗で桁を移していく構造なので、0.1を正確に表すことができません。ということで、ふつうの浮動小数点数で演算する限り、誤差は避けがたいものとなります。

この場合でしたら、ループを整数で回して、都度10で割っていくとスッキリすると思います。

php

1for ($i = 0; $i < 100; $i++) { 2 var_dump([$i / 10.0 , floor($i / 10.0)]); 3}

投稿2016/10/12 06:43

maisumakun

総合スコア145123

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2016/10/12 06:51

質問文にあるソースはあくまで今回の事象を確認するためのもので、これをなんとかしたいわけじゃないと思うし、じゃぁどういう対策を取ったらいいですかという質問なんだと思います。
maisumakun

2016/10/12 06:55

とりあえず、「浮動小数点数でループを回す」というのは(仮にサンプルで作った例だったとしても)決定的にまずいので、そこは改善してほしいと思いました。
退会済みユーザー

退会済みユーザー

2016/10/12 07:04

解答ありがとうございます。 会計システムでしたので消費税など比率の値が多くなってしまい途中計算などで浮動小数点数出てきてしまって。 DBにはdecimalを用いているのですがPHPにもってくるとfloatになってしまい扱いに困ってしまいました。 今現在は都度10の乗数で整数にしてから計算しているのですが計算ミスが多くて困っている状況です。
退会済みユーザー

退会済みユーザー

2016/10/12 07:05

まぁ、0.1を足し続けるというのが精度的にヤバいということには一定の同意をしますが、 (本筋から外れますが)forループで扱う式が整数値以外も扱えることも面白いPHPですね。 for($col = 'R'; $col != 'AD'; $col++) { echo $col.' '; } とかもアリなんだそうで。
guest

0

PHP5.4.45環境とPHP7.0.11で質問文のソースコードをコピペしましたが、こちらでは問題なく動作していました。
質問文の結果は、質問文のソースコードの結果そのままコピペしたモノでしょうか。
($iが整数部分以外を除去した以外は)

array(2) { [0]=> float(0) [1]=> float(0) } array(2) { [0]=> float(0.1) [1]=> float(0) } ・・・ array(2) { [0]=> float(1) [1]=> float(0) } array(2) { [0]=> float(1.1) [1]=> float(1) }

投稿2016/10/12 06:47

kunai

総合スコア5405

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2016/10/12 07:15

解答ありがとうございます。 貼ってもらった値見ると同じ現象のように見えます。 演算で得られた値が整数でも浮動小数点数の型である場合 floor の関数を用いると値がずれてしまって困っているのです。 つまり 値が 1 のときに floor を用いると 0 になっていると困ってしまいます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問