サーバー上で数字を表示する場合、PHPに限らずjavascriptでも、小数点表記で表示できるのは、小数点以下4桁までのようです。5桁になると、指数表記に変換されてしまうんです。
例えば以下のプログラムを実行すると、小数点以下5桁以上は、指数表記に変換されるのがよくわかります。
<?php
//京都
$x = 0.0001;
var_dump($x);
$y = 0.00001;
var_dump($y);
?>
画面表示:float(0.0001) float(1.0E-5)
参考:デモプログラムa
この指数表記を小数点表記に変換する方法は、sprintfを使って以下のコードを書けばOKです。
<?php
//京都
$x = 0.0001;
var_dump($x);
$y = 0.00001;
$a = "%.5f";
$y_b = sprintf($a, $y);
var_dump($y_b);
?>
参考:デモプログラムb
しかし、小数点以下の桁数が予め分かっていれば上のコードで何ら問題無いのですが、分かっていない場合は使いづらいです。そこで小数点以下の桁数が分かっていなくても、小数表記に変換出来る関数を作りましたので参考にして下さい。
まずは、入力された数字自体が指数表記になっていた場合に、少数表記へ変換する関数です。
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も使っています。
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;
}
これらの関数を組み合わせると、指数表記から小数表記へ変換出来ます。
<?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
おまけで、小数点以下の足し算で、指数表記へ変換されない関数も書いておきます。上の関数に加えて、小数点以下の桁数を取得する関数が必要です。コードは以下の通りです。
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;
}
これらの関数を組み合わせて、以下のコードが完成します。
<?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
以上。
そもそも、小数表記から指数表記へ自動変換されない方法を探したのですが、どうやらありませんでした。その後、私も色々、指数表記された数字同士を、誤差を少なく加減乗除する関数を検索したのですが、見つからず。
試行錯誤した結果、上の関数に行き着きました。同じ悩みを持っている方のお役に立てますように。