PHP에서 부동 소수점(float) 값의 동등 여부를 비교하는 것은 생각보다 까다로운 작업입니다. 겉보기에 같은 값을 가진 부동 소수점 숫자라도 내부적으로 저장되는 방식의 특성 때문에 실제로는 미세하게 다른 값일 수 있기 때문입니다. 따라서 단순히 == 연산자로 비교하면 예상과 다른 결과가 나올 수 있습니다.
이 글에서는 PHP에서 부동 소수점 값을 비교할 때 발생하는 문제를 살펴보고, 이를 해결하기 위한 두 가지 실용적인 방법을 예제와 함께 소개합니다.
문제 상황 예제
간단한 예제를 통해 문제를 확인해 보겠습니다:
<?php
$a = 0.14;
$b = 1 - 0.86; // 0.14
if($a == $b ){
echo 'a and b are same';
}
else {
echo 'a and b are not same';
}
?>
출력 결과
a and b are not same.
원인 분석
$a와 $b는 수학적으로 동일한 값(0.14)임에도 불구하고 else 블록이 실행되었습니다. 그 이유는 부동 소수점 숫자가 컴퓨터 내부에서 이진수(binary) 형태로 근사치로 표현되기 때문입니다. 0.14라는 십진수 값은 이진수로 정확하게 표현될 수 없어, 연산 과정에서 아주 작은 오차가 누적됩니다. 결과적으로 $a와 $b의 내부 값은 미세하게 달라지고, 일치 비교는 실패하게 됩니다.
그렇다면 실무에서 이러한 문제를 어떻게 피할 수 있을까요? 대표적인 두 가지 방법을 알아보겠습니다.
방법 1: 허용 오차(Epsilon)를 이용한 비교
첫 번째 방법은 두 값의 차이가 아주 작은 임계값(epsilon)보다 작으면 같은 값으로 간주하는 것입니다. 이는 컴퓨터 시스템에서 계산 가능한 최소 차이 개념을 활용한 방식으로, 부동 소수점 비교의 가장 일반적인 해결책입니다.
예제
<?php
$val1 = 2.183459;
$val2 = 2.183450;
$epsilon = 0.00001;
if(abs($val1 - $val2) < $epsilon) {
echo "True";
}
else {
echo "False";
}
?>
출력 결과
True
설명
위 코드에서는 두 개의 부동 소수점 변수 $val1과 $val2, 그리고 허용 오차값인 $epsilon을 선언했습니다. PHP의 내장 함수인 abs()를 사용해 두 값의 차이의 절댓값을 구하고, 이 값이 epsilon보다 작은지 검사합니다.
절댓값을 사용하는 이유는 두 값 중 어느 쪽이 큰지 알 수 없기 때문입니다. 절댓값을 적용하면 차이의 크기만 비교하므로 부호와 무관하게 항상 올바른 판단이 가능합니다. 위 예제에서 두 값은 소수점 다섯 자리까지 동일하므로, 그 차이는 epsilon(0.00001)보다 작아 'True'가 출력됩니다.
방법 2: round() 함수를 이용한 반올림 비교
두 번째 방법은 PHP의 내장 함수인 round()를 사용하여 값을 지정된 소수점 자릿수로 반올림한 후 비교하는 것입니다. 미세한 오차를 사전에 제거함으로써 안정적인 비교가 가능합니다.
예제
<?php
$val1 = 9 - 6.2;
$val2 = 1.8;
var_dump(round($val1, 2) == round($val2, 2));
?>
출력 결과
bool(true)
설명
여기서도 두 개의 부동 소수점 변수 $val1(9 - 6.2)과 $val2(1.8)를 사용했습니다. 이론적으로 두 값은 모두 1.8이지만, 앞서 설명한 것처럼 내부적으로는 미세한 오차를 포함하고 있습니다.
round() 함수에 두 번째 인자로 2를 전달하면 값을 소수점 둘째 자리까지 반올림합니다. 반올림 과정에서 미세한 오차가 제거되므로, 이후 == 연산자로 비교하면 기대한 대로 bool(true)가 출력됩니다.
마무리
부동 소수점 비교 문제는 PHP뿐만 아니라 대부분의 프로그래밍 언어에서 공통적으로 나타나는 현상입니다. 핵심은 부동 소수점 값이 근사치로 표현된다는 점을 인지하고, 허용 오차(epsilon) 비교 또는 round() 함수를 통한 반올림 후 비교 방식을 상황에 맞게 활용하는 것입니다. 금융 계산처럼 정확성이 중요한 경우에는 부동 소수점 대신 BCMath 확장 모듈이나 문자열 기반 연산을 고려하는 것도 좋은 선택입니다.