PHP | 부동 소수점 오류 해결법

|

요약

컴퓨터는 모든 숫자를 2진수로 표현하기 때문에
부동 소수점에 약간의 오차범위가 생기게 됩니다.

현상 확인

<?php
echo floor( 2.0 * 100 )."<br>";
echo floor( 2.1 * 100 )."<br>";
echo floor( 2.2 * 100 )."<br>";
echo floor( 2.3 * 100 )."<br>"; // 부동소수점 오류
echo floor( 2.4 * 100 )."<br>";
echo "<br>";

echo ((2.0 * 100) % 10 )."<br>";
echo ((2.1 * 100) % 10 )."<br>";
echo ((2.2 * 100) % 10 )."<br>";
echo ((2.3 * 100) % 10 )."<br>"; // 부동소수점 오류
echo ((2.4 * 100) % 10 )."<br>";
?>

200
210
220
229
240

0
0
0
9
0

값이 이렇게 뛰는 현상이 생기게 됩니다. 이 때 해결하는 방법은 bcmath 나 gmp 를 활용하는 방법이 있습니다.

0.15 출력 에러 이슈

0.15는 순환소수, 0.16은 유한소수입니다. 컴퓨터는 2진수로만 표현하기 때문에 순환소수의 경우에는 깔끔하게 값이 떨어지지 않게 됩니다. 이러한 문제를 해결하기 위해서는 int 로 활용할 수 있도록 값을 정수로 바꾸어 계산하는 등의 작업을 거쳐야 합니다.

유한소수, 순환소수, 무한소수

  • 유한소수
    분수를 소수로 바꿨을 때 숫자가 계속되지 않고 멈추는 소수
    ex) 0.5
  • 순환소수 (유리수)
    무한소수중에서도 반복되는 패턴을 가지면 순환소수라고 합니다.
    ex) 0.3333… 0.4783483483…
  • 순환하지 않는 무한소수 (무리수)
    소수점 아래의 값이 0이 아닌 숫자가 무한히 많은 소수입니다. 반복되지 않습니다.
    ex) √2=1.41421356…, √3=1.73205080…, π=3.1415926

데이터베이스 타입 변환

  • float 으로 되어있다면 decimal(소수) 로 변환하여 사용하는 방법
  • 0.15를 * 1000하여 int 형식으로 저장

소수점으로 저장이 되었

DB 쿼리에서 연산하기

계산의 정확도를 따지면 DB에서 쿼리로 계산하는 것이 유리할 수도 있습니다.
유지보수에 유리한 방향으로 설계하는 것이 중요하니 도입하기 전 여러가지로 고려를 한 뒤 적용하는 것이 좋습니다.

select cast(amount * 100 as int) as "Cents"
from shopkeeper  where createddate  between date_trunc('day', now()) - interval '1 day'  and  date_trunc('day', now())

SQL 구문에서 계산을 하게 되면 연산에 에러가 덜 뜨게 만들 수 있으니 한번 고려하는 것도 방법이라 생각합니다.

문자열로 변환

strval() 함수를 사용하여 문자형으로 변경하여 해결하는 방법입니다.
성능에 제약을 줄 수 있기 때문에 이런 방법도 있다. 라고 봐주시면 좋을 것 같아요

strval($data)-strval($data)

참조

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

클릭하여 복사