Integer check in PHP
I'm writing an odds converter in PHP and came across this code for converting decimal odds to fractional ones
function dec2frac($dec) {
$decBase = --$dec;
$div = 1;
do {
$div++;
$dec = $decBase * $div;
} while (intval($dec) != $dec);
if ($dec % $div == 0) {
$dec = $dec / $div;
$div = $div / $div;
}
return $dec.'/'.$div;
}
When I tested the code, it would sometimes succeed in the calculations and sometime try to load the page for some time without success so I figured that it got stuck in the loop somehow. I set the time limit to 1 second and an echo in the loop, which confirmed my suspicion. I added these two echoes to the loop so I could see what went wrong.
echo "$dec $div<br/>";
echo var_dump(intval($dec) == $dec)." $dec is int<br/>";
Example printout when it fails, using decimal = 1.6
1.2 2
bool(false) 1.2 is int
1.8 3
bool(false) 1.8 is int
2.4 4
bool(false) 2.4 is int
3 5
bool(false) 3 is int //should break here and return 3/5
3.6 6
bool(false) 3.6 is int
4.2 7
bool(false) 4.2 is int
Example printout when it succeeds, using decimal = 1.8
1.6 2
bool(false) 1.6 is int
2.4 3
bool(false) 2.4 is int
3.2 4
bool(false) 3.2 is int
4 5
bool(true) 4 is int
Why doesn't it recognize the integers sometimes? How can I fix it so it exits the loop when an integer is found?
It looks like a floating precision rounding error. intval($dec) and $dec appear to be equal, but actually aren't. The difference is negligible but strict equalness fails.
On my 64bit system, with a precision of 1E-15 the function works, with a precision of 1E-16 it loops.
In general, strict comparison of two floating point numbers is to be avoided. Two floats are to be considered "equal" if their difference is less than a threshold. There are ways of calculating this threshold (google for "determining machine precision"), for it is not the same everywhere.
Since in my PHP.INI I have the default value
; The number of significant digits displayed in floating point numbers.
; http://php.net/precision
precision = 14
then the two numbers are shown equal even if one is 3 and the other 3.0000000000044, and the caltrop goes unnoticed.
With precision = 18, $dec
is shown not to be what you'd expect:
1.20000000000000018
1.80000000000000027
2.40000000000000036
3.00000000000000044
Try:
function dec2frac($dec) {
$decBase = --$dec;
$div = 1;
do {
$div++;
$dec = $decBase * $div;
} while (abs(intval($dec) - $dec) > 1e-15);
if (($dec % $div) == 0) {
$dec = $dec / $div;
$div = $div / $div;
}
return $dec.'/'.$div;
}
链接地址: http://www.djcxy.com/p/59148.html
上一篇: 在Ruby中什么是::(双冒号)?
下一篇: 在PHP中整数检查