>백엔드 개발 >PHP 튜토리얼 >PHP 부동 소수점 정밀도 문제_php 팁 요약

PHP 부동 소수점 정밀도 문제_php 팁 요약

WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWB
WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWB원래의
2016-05-16 20:15:431126검색

1. PHP 부동 소수점 정밀도 손실 문제

먼저 다음 코드를 살펴보세요.

코드 복사 코드는 다음과 같습니다.

$f = 0.57;
echo intval($f * 100) //56

결과가 다소 놀라울 수도 있습니다. PHP는 IEEE 754 배정밀도 를 따릅니다.

64비트 배정밀도의 부동 소수점 수는 1개의 부호 비트(E), 11개의 지수 비트(Q) 및 52비트 가수(M)(총 64비트)로 표현됩니다.
부호 비트: 가장 높은 비트는 데이터의 부호를 나타내고, 0은 양수, 1은 음수를 나타냅니다.
지수 비트 : 2진수로 거듭제곱한 데이터를 나타내며, 지수는 오프셋 코드로 표현됩니다
가수 : 데이터의 소수점 이하 유효숫자를 나타냅니다.

십진수를 이진수로 표현하는 방법을 살펴보겠습니다.

2를 곱하고 반올림하여 순서대로 배열합니다. 즉, 소수부에 2를 곱한 다음 정수부를 취하고, 계속해서 나머지 소수부에 2를 곱하고, 그 다음 정수부를 취하고, 나머지 소수부를 곱합니다. 2로 나눠서 계속해서 소수점까지 취하는데, 이렇게 0.57과 같은 소수점을 곱하면 소수부는 0이 될 수 없습니다. 유효숫자의 소수점 표현은 이진수로 무한대입니다.

0.57의 이진 표현은 기본적으로(52비트)입니다: 0010001111010111000010100011110101110000101000111101

52비트만 있으면 0.57 => 0.56999999999999995

위에서 예상치 못한 결과를 보는 것은 어렵지 않습니다.

2. PHP 부동 소수점 수의 정밀도 문제

먼저 질문을 살펴보겠습니다.

코드 복사 코드는 다음과 같습니다.

$f = 0.58;
var_dump(intval($f * 100)); //57이 출력되는 이유

많은 학생들이 이런 질문을 했을 거라 생각합니다.

구체적인 원리에 대해서는 "Brother Bird"의 기사에서 자세한 설명을 읽을 수 있습니다. PHP 부동 소수점 숫자에 대한 일반적인 질문에 대한 답변

그렇다면 이 문제를 피하는 방법은 무엇일까요?
여러 가지 방법이 있습니다. 다음은 두 가지입니다.
1. 스프린트

코드 복사 코드는 다음과 같습니다.

substr(sprintf("%.10f", ($a/ $b)), 0, -7);

2. 둥글게(반올림되오니 주의하세요)
코드 복사 코드는 다음과 같습니다.

라운드($a/$b, 3);

아니면 더 좋은 방법이 있으면 메시지 남겨서 알려주세요.

3. PHP 부동 소수점 숫자에 대한 일반적인 질문에 대한 답변

PHP의 부동 소수점 숫자에 관해 이전에 다음 기사를 쓴 적이 있습니다. PHP의 부동 소수점에 대한 모든 '가짜'(PHP의 부동 소수점에 대한 모든 '가짜')

그런데 제가 놓친 것이 하나 있는데, 바로 이 일반적인 질문에 대한 답입니다.

코드 복사 코드는 다음과 같습니다.

$f = 0.58;
var_dump(intval($f * 100)); //57이 출력되는 이유
?>

왜 출력이 57인가요? PHP 버그인가요?

bugs.php.net에 자주 질문하는 것은 물론, 비슷한 질문을 하는 분들이 많아서 많은 학생들이 이런 질문을 했을 거라 믿습니다...

이 이유를 이해하려면 먼저 부동 소수점 숫자의 표현을 알아야 합니다(IEEE 754).

예를 들어 64비트 길이(이중 정밀도)를 사용하는 부동 소수점 숫자는 1개의 부호 비트(E), 11개의 지수 비트(Q) 및 52비트 가수(M)로 표시됩니다(총 64비트).

부호 비트: 가장 높은 비트는 데이터의 부호를 나타내며, 0은 양수, 1은 음수를 나타냅니다.

지수 비트 : 2진수로 거듭제곱한 데이터를 나타내며, 지수는 오프셋 코드로 표현됩니다.

가수 : 데이터의 소수점 이하 유효숫자를 나타냅니다.

여기서 핵심은 십진수를 이진수로 표현하는 것입니다. 십진수를 이진수로 표현하는 방법은 Baidu에서 검색할 수 있습니다. 여기서는 이진수에 대해 자세히 설명하지 않겠습니다. 표현, 0.58은 무한합니다. 긴 값(아래 숫자는 암시적 1을 생략함)..

0.58의 이진 표현은 기본적으로(52비트)입니다: 0010100011110101110000101000111101011100001010001111
0.57의 이진 표현은 기본적으로(52비트)입니다: 0010001111010111000010100011110101110000101000111101
이 52비트를 통해서만 계산할 경우 둘의 이진수는 다음과 같습니다.

코드 복사 코드는 다음과 같습니다.

0.58 -> 0.57999999999999996
0.57 -> 0.56999999999999995

0.58*100의 구체적인 부동소수점 곱셈에 대해서는 자세히 다루지 않겠지만 관심 있는 분들은 보시면 되겠지만(부동소수점) 암산을 통해 막연하게 살펴보겠습니다. ... 0.58 * 100 = 57.999999999

그럼 인테이크하면 자연스럽게 57이 되겠죠…

이 문제의 핵심은 "유한해 보이는 소수는 컴퓨터의 이진 표현에서 실제로는 무한하다"는 점임을 알 수 있습니다.

이것이 PHP 버그라고 생각하지 마세요. 이것이 바로...

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.