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中整数检查