다루는 값이 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
'PHP' 카테고리의 다른 글
[PHP] json pretty print (0) | 2014.03.18 |
---|---|
[PHP] MongoDB의 _id로 기간 query 하기 (ObjectId, timestamp, interval, range) (0) | 2014.01.24 |
[PHP] PHP 소스코드로 MongoDB의 sh.status (db.printShardingStatus) 정보 출력하기 (php, MongoDB, sh.status, db.printShardingStatus) (0) | 2013.11.04 |
[PHP] 유니코드 문자열 한글판별 (unicode, hangul) (0) | 2013.10.30 |
[PHP] 소스 레벨에서 표준 I/O 스트림 리다이렉트 (STDIN, STDOUT, STDERR redirect) (0) | 2013.08.19 |