다루는 값이 PHP_INT_MAX를 넘어서고, 소수점도 들어가고 해서 float 으로 변환해서 계산하고 있었는데,

추후 데이터 보정 과정에서 단순 비교를 하다가 피를 본 사연.

 

var_dump 같은 걸로 출력해보면 같은 값으로 나오는데, 비교연산을 해보면 다르다고 나옴.

부동소수점의 특성상 절대로 일반적인 비교연산은 금물.

 

일단 결론부터 말하자면 epsilon 보정을 해줘야 함.

 

// 어디선가 계산을 통해 뽑아져 나온 $f1, $f2 라는 변수가 있다고 가정함.

 

// 단순비교시 false

var_dump($f1 == $f2);

 

// 아래처럼 epsilon 보정을 해줘야 함.

function isSame($f1, $f2, $epsilon=0.00001)

{

    return abs(($f1 - $f2) / $f2) < $epsilon;

}

 

// 아래는 true ($f1과 $f2의 값의 차이가 0.00001 을 넘지 않는다는 가정 하에

var_dump(isSame($f1, $f2));

 

 

 

[부록] 궁금해서 $f1, $f2 값을 각각 바이너리로 뽑아봤더니 끝에서 미묘하게 달랐음.

// 원래 값은 6894142036.84

var_dump($f1, $f2);

 

// float to 0101010101...

function floatToBinStr($val)

{

    $bin = '';

    $packed = pack('d', $val); // use 'f' for 32bit

    foreach (str_split(strrev($packed)) as $char) {

        $bin .= str_pad(decbin(ord($char)), 8, 0, STR_PAD_LEFT);

    }

    return $bin;

}

 

// 아래 출력값은 각각 아래와 같음

// $f1 = 0100000111111001101011101100010000100101010011010111000010011110

// $f2 = 0100000111111001101011101100010000100101010011010111000010100100

var_dump(floatToBinStr($f1), floatToBinStr($f2));

 

 

 

[참조]

http://php.net/manual/en/language.types.float.php

http://stackoverflow.com/questions/3148937/compare-floats-in-php

 

 

 

 

 

 

 

 

 

 

 

 

 

Posted by bloodguy
,