문제

PHP의 플로트 데이터 유형이 부정확하고 MySQL의 플로트는 int보다 더 많은 공간을 차지하기 때문에 (그리고 부정확 한), 나는 항상 2 진수의 정밀도를 보장하기 위해 저장하기 전에 ints로 가격을 곱한 가격으로 항상 저장합니다. . 그러나 나는 PHP가 잘못 행동하고 있다고 생각합니다. 예제 코드 :

echo "<pre>";

$price = "1.15";
echo "Price = ";
var_dump($price);

$price_corrected = $price*100;
echo "Corrected price = ";
var_dump($price_corrected);


$price_int = intval(floor($price_corrected));
echo "Integer price = ";
var_dump($price_int);

echo "</pre>";

생산 된 출력 :

Price = string(4) "1.15"
Corrected price = float(115)
Integer price = int(114)

놀랐습니다. 최종 결과가 1에 의해 예상보다 낮았을 때 테스트의 출력이 더 많이 보일 것으로 예상했습니다.

Price = string(4) "1.15"
Corrected price = float(114.999999999)
Integer price = int(114)

플로트 유형의 부정확성을 보여줍니다. 그러나 왜 바닥 (115)이 114로 돌아 오는 이유는 무엇입니까 ??

도움이 되었습니까?

해결책

빠른 수정으로 이것을 시도하십시오.

$price_int = intval(floor($price_corrected + 0.5));

당신이 겪고있는 문제는 PHP의 결함이 아니며, 부동 소수점 산술을 가진 실수를 사용하는 모든 프로그래밍 언어는 비슷한 문제가 있습니다.

금전적 계산의 일반적인 경험 법칙은 플로트를 사용하지 않는 것입니다 (데이터베이스 나 스크립트에서는). 항상 달러 대신 센트를 저장하여 모든 종류의 문제를 피할 수 있습니다. 센트는 정수이며 자유롭게 함께 추가하고 다른 정수를 곱할 수 있습니다. 숫자를 표시 할 때마다 마지막 두 자리 숫자 앞에 점을 삽입해야합니다.

115 대신 114를 얻는 이유는 floor 가장 가까운 정수를 향해 바닥 (114.99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999U를로 UANDS). 조금 적을 수 없으므로 100을 곱하면 115보다 작은 숫자를 얻습니다.

다음은 무엇을 더 자세한 설명입니다 echo 1.15 * 100; 하다:

  • 1.15를 바이너리 플로팅 포인트 번호로 구문 분석합니다. 여기에는 반올림이 포함되며, 이진 부동 소수점 번호를 1.15에 가장 가까운 바이너리 플로팅 포인트 수를 얻기 위해 조금 반올림합니다. 반올림 오류없이 정확한 숫자를 얻을 수없는 이유는 1.15가 기본 2에 무한 수의 숫자를 가지기 때문입니다.
  • 이진 부동물 지점 번호로 100을 구문 분석합니다. 여기에는 반올림이 포함되지만 100은 작은 정수이므로 반올림 오류는 0입니다.
  • 이전 두 숫자의 제품을 계산합니다. 여기에는 가장 가까운 바이너리 플로팅 포인트 번호를 찾기 위해 약간의 반올림이 포함됩니다. 이 작업에서 반올림 오류가 0이됩니다.
  • 바이너리 플로팅 포인트 번호를 도트로베이스 10 소수수 번호로 변환 하고이 표현을 인쇄합니다. 여기에는 약간의 반올림이 포함됩니다.

PHP가 놀라운 것을 인쇄하는 이유 Corrected price = float(115) (114.999 대신 ...)입니다 var_dump 정확한 번호를 인쇄하지는 않지만 둥근 숫자를 인쇄합니다. n - 2 (또는 n - 1) 숫자, 여기서 n 자리는 계산의 정밀도입니다. 쉽게 확인할 수 있습니다.

echo 1.15 * 100;  # this prints 115
printf("%.30f", 1.15 * 100);  # you 114.999....
echo 1.15 * 100 == 115.0 ? "same" : "different";  # this prints `different'
echo 1.15 * 100 < 115.0 ? "less" : "not-less";    # this prints `less'

플로트를 인쇄하는 경우 다음을 기억하십시오. 플로트를 인쇄 할 때 항상 모든 숫자를 보지는 않습니다..

또한 시작 부분 근처의 큰 경고도 참조하십시오. PHP 플로트 문서.

다른 팁

다른 답변은 그 원인과 문제에 대한 좋은 해결 방법을 다루었습니다.

다른 각도에서 문제를 해결하는 것을 목표로합니다.

MySQL에 가격 가치를 저장하려면 아마도 십진 유형, 이를 통해 정확한 값을 소수점 이하의 장소로 저장할 수 있습니다.

PHP는 상당한 숫자를 기준으로 반올림을하고 있습니다. 부정확성을 숨기고 있습니다 (2 행). 물론, 바닥이 올 때, 그것은 더 나은 것을 알지 못하고 그것을 끝까지 랑핑합니다.

어쩌면이 "문제"에 대한 또 다른 가능한 솔루션 일 수도 있습니다.

intval(number_format($problematic_float, 0, '', ''));

언급 한 바와 같이, 이것은 PHP 그 자체의 문제가 아니라고 말하면, 유한 한 부동 소수점 값으로 표현할 수없는 분수를 처리하는 데 더 많은 문제가 있으므로 반올림시 캐릭터의 손실을 초래합니다.

해결책은 부동 소수점 값을 작업 할 때 정확도를 유지해야 할 때 GMP 기능 또는 BC 수학 기능을 사용해야합니다. BCPOW, BCMUL et al. 그리고 문제는 쉽게 해결 될 것입니다.

예를 들어 $ price_correstrected = $ price*100 대신;

$ price_corrugreced = bcmul ($ price, 100);

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top