サーバー上で数字を表示する場合、PHPに限らずjavascriptでも、小数点表記で表示できるのは、小数点以下4桁までのようです。5桁になると、指数表記に変換されてしまうんです。
例えば以下のプログラムを実行すると、小数点以下5桁以上は、指数表記に変換されるのがよくわかります。
1 2 3 4 5 6 7 8 9 10 |
<?php //京都 $x = 0.0001; var_dump($x); $y = 0.00001; var_dump($y); ?> |
画面表示:float(0.0001) float(1.0E-5)
参考:デモプログラムa
この指数表記を小数点表記に変換する方法は、sprintfを使って以下のコードを書けばOKです。
1 2 3 4 5 6 7 8 9 10 11 12 |
<?php //京都 $x = 0.0001; var_dump($x); $y = 0.00001; $a = "%.5f"; $y_b = sprintf($a, $y); var_dump($y_b); ?> |
参考:デモプログラムb
しかし、小数点以下の桁数が予め分かっていれば上のコードで何ら問題無いのですが、分かっていない場合は使いづらいです。そこで小数点以下の桁数が分かっていなくても、小数表記に変換出来る関数を作りましたので参考にして下さい。
まずは、入力された数字自体が指数表記になっていた場合に、少数表記へ変換する関数です。
1 2 3 4 5 6 7 8 9 |
function index_to_decimal($str){ if( preg_match('@\.(\d+)E\-(\d+)@',$str,$matches) ){ $digit = strlen($matches[1])+$matches[2]; $format = "%.".$digit."f"; $str = sprintf($format,$str); return $str; } return $str; } |
次に、{0.001000}を{0.001}へ、{100.}を{100}へ整形する関数です。コードは以下の通りです。上のindex_to_decimalも使っています。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function remove_back_zero($str){ $str = index_to_decimal($str); if( preg_match('@\.@',$str,$matches) ){ $patterns = array(); $patterns[0] = '@0+$@'; $patterns[1] = '@\.+$@'; $replacements = array(); $replacements[2] = ''; $replacements[1] = ''; return $str = preg_replace($patterns, $replacements, $str); } return $str; } |
これらの関数を組み合わせると、指数表記から小数表記へ変換出来ます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<?php //京都 $x = 0.0000005677800; var_dump($x); var_dump(remove_back_zero($x)); function index_to_decimal($str){ if( preg_match('@\.(\d+)E\-(\d+)@',$str,$matches) ){ $digit = strlen($matches[1])+$matches[2]; $format = "%.".$digit."f"; $str = sprintf($format,$str); return $str; } return $str; } function remove_back_zero($str){ $str = index_to_decimal($str); if( preg_match('@\.@',$str,$matches) ){ $patterns = array(); $patterns[0] = '@0+$@'; $patterns[1] = '@\.+$@'; $replacements = array(); $replacements[2] = ''; $replacements[1] = ''; return $str = preg_replace($patterns, $replacements, $str); } return $str; } ?> |
参考:デモプログラムc
おまけで、小数点以下の足し算で、指数表記へ変換されない関数も書いておきます。上の関数に加えて、小数点以下の桁数を取得する関数が必要です。コードは以下の通りです。
1 2 3 4 5 6 7 8 9 |
function conjectrue_digits($str) { $str = remove_back_zero($str); preg_match('@\.(\d+)@',$str,$matches); if($matches){ $digit = strlen($matches[1]); }else{$digit=0;} return $digit; } |
これらの関数を組み合わせて、以下のコードが完成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
<?php //京都 $x = 0.0000005677800; $y = 0.00001231111; $sum = $x+$y; var_dump($sum); //$xと$yの小数点以下の桁数を取得。そして、大きい方の桁数を取得 $digit_x = conjectrue_digits($x); $digit_y = conjectrue_digits($y); $digit_max = max($digit_x,$digit_y); //小数点以下の数字をそのまま足し算すると、誤差が生じるために、二つの数字を整数へ変換 $pow = pow(10,$digit_max); $x_b = $x * $pow; $y_b = $y * $pow; //整数同士で足し算をした後に、小数へ戻す $sum = ($x_b+$y_b)/$pow; $format = "%.".$digit_max."f"; $sum = sprintf($format,$sum); var_dump($sum); function index_to_decimal($str){ if( preg_match('@\.(\d+)E\-(\d+)@',$str,$matches) ){ $digit = strlen($matches[1])+$matches[2]; $format = "%.".$digit."f"; $str = sprintf($format,$str); return $str; } return $str; } function remove_back_zero($str){ $str = index_to_decimal($str); if( preg_match('@\.@',$str,$matches) ){ $patterns = array(); $patterns[0] = '@0+$@'; $patterns[1] = '@\.+$@'; $replacements = array(); $replacements[2] = ''; $replacements[1] = ''; return $str = preg_replace($patterns, $replacements, $str); } return $str; } function conjectrue_digits($str) { $str = remove_back_zero($str); preg_match('@\.(\d+)@',$str,$matches); if($matches){ $digit = strlen($matches[1]); }else{$digit=0;} return $digit; } ?> |
参考:デモプログラムd
以上。
そもそも、小数表記から指数表記へ自動変換されない方法を探したのですが、どうやらありませんでした。その後、私も色々、指数表記された数字同士を、誤差を少なく加減乗除する関数を検索したのですが、見つからず。
試行錯誤した結果、上の関数に行き着きました。同じ悩みを持っている方のお役に立てますように。