>  기사  >  백엔드 개발  >  PHP 부동 소수점 정밀도에 관한 질문

PHP 부동 소수점 정밀도에 관한 질문

怪我咯
怪我咯원래의
2017-07-11 13:56:103287검색

C언어와 C#언어에서는 부동소수점형의 데이터를 단정밀도형(float), 이중정밀도형(double)을 사용하여 저장하며, double형 데이터는 64비트를 차지한다고 선언하고 있습니다. 변수 float f= 2.25f일 때 메모리는 어떻게 할당됩니까? 무작위로 할당하면 세상이 혼란스럽지 않을까요? 사실 float와 double은 모두 IEEE R32.24를 따르고, double은 R64.53을 따릅니다.

단정밀도이든 배정밀도이든 저장은 세 부분으로 나뉩니다.

부호 비트(Sign): 0은 양수를 나타내고 1은 음수를 나타냅니다. 시프트 저장 사용

Mantissa(Mantissa): Mantissa 부분

이 글은 주로 PHP 부동 소수점 수 정확도 문제에 대한 요약을 소개합니다. 이 글은 세 가지 다른 문단을 사용하여 PHP 부동 소수점 수 정확도 손실 문제에 중점을 두고 있습니다. 이 문제의 원인과 해결 방법은 도움이 필요한 친구들이 참고할 수 있습니다

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이 될 수 없습니다. 유효숫자의 소수부는 2진수로 표현하면 무한대입니다. 0.57의 이진 표현은 기본적으로(52비트): 0010001111010111000010100011110101110000101000111101

만 52비트인 경우 0.57 => 0.5699999999999999 5

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

2. PHP 부동 소수점 숫자의 정확성 문제

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

코드는 다음과 같습니다.

$f = 0.58;
var_dump(intval($f * 100)); //为啥输出57
많은 학생들이 이런 질문을 했을 거라 믿습니다.

특정 원리에 대해서는 "Brother Bird"의 기사에서 자세한 설명을 읽을 수 있습니다.

FAQ

PHP 부동 소수점 숫자에 대한 답변 그렇다면 이 문제를 피하는 방법은 무엇일까요?

여러 가지 방법이 있습니다. 다음은 두 가지입니다.

1.
sprintf

코드는 다음과 같습니다.

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

2.round(반올림됩니다.)


코드는 다음과 같습니다.

round($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비트)입니다 1100001010001111 0101110000101000111101

그리고 계산하면 둘의 이진수




코드 복사

코드는 다음과 같습니다.

0.58 -> 0.57999999999999996
0.57 -> 0.56999999999999995
0.58 * 100의 특정 부동 소수점 곱셈에 대해서는 자세히 고려하지 않습니다. (부동소수점)을 보면 암산으로 막연하게 살펴보게 되는데... 0.58 * 100 = 57.999999999

그러면 자연스럽게 57이 되겠죠…

이 문제의 핵심은 다음과 같습니다. "유한해 보이는 소수는 컴퓨터의 이진수 표현에서 실제로는 무한합니다."

위 내용은 PHP 부동 소수점 정밀도에 관한 질문의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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