[php] 해석 언어에서 매우 큰 정수로 작업 할 때 예기치 않은 결과
의 합을 얻으려고 1 + 2 + ... + 1000000000
하지만 PHP와 Node.js 에서 재미있는 결과를 얻습니다 .
PHP
$sum = 0;
for($i = 0; $i <= 1000000000 ; $i++) {
$sum += $i;
}
printf("%s", number_format($sum, 0, "", "")); // 500000000067108992
Node.js
var sum = 0;
for (i = 0; i <= 1000000000; i++) {
sum += i ;
}
console.log(sum); // 500000000067109000
정답은 다음을 사용하여 계산할 수 있습니다.
1 + 2 + ... + n = n(n+1)/2
정답 = 500000000500000000 이므로 다른 언어를 사용하기로 결정했습니다.
가다
var sum , i int64
for i = 0 ; i <= 1000000000; i++ {
sum += i
}
fmt.Println(sum) // 500000000500000000
그러나 잘 작동합니다! PHP와 Node.js 코드에 어떤 문제가 있습니까?
아마도 이것은 해석되는 언어의 문제 일 것이므로 Go와 같은 컴파일 된 언어로 작동합니까? 그렇다면 Python 및 Perl과 같은 다른 해석 언어도 같은 문제가 있습니까?
답변
파이썬 작동 :
>>> sum(x for x in xrange(1000000000 + 1))
500000000500000000
또는:
>>> sum(xrange(1000000000+1))
500000000500000000
파이썬의 int
자동 long
은 임의의 정밀도를 지원 하는 파이썬으로 승격합니다 . 32 또는 64 비트 플랫폼에서 정답을 생성합니다.
이는 플랫폼의 비트 폭보다 훨씬 큰 2를 2로 올리면 알 수 있습니다.
>>> 2**99
633825300114114700748351602688L
PHP에서 잘못된 값이 2 ** 32-1보다 큰 경우 PHP가 부동으로 승격하기 때문에 오류가 있음을 입증 할 수 있습니다.
>>> int(sum(float(x) for x in xrange(1000000000+1)))
500000000067108992
답변
Go 코드는 정확한 답을 제공하기에 충분한 비트를 가진 정수 산술을 사용합니다. PHP 나 Node.js를 만지지 않았지만 결과에서 수학은 부동 소수점 숫자를 사용하여 수행 된 것으로 생각 되므로이 크기의 숫자에는 정확하지 않아야합니다.
답변
정수 변수의 sum
값이 최대 값을 초과 하기 때문입니다 . 그리고 sum
당신은 반올림을 포함하는 부동 소수점 산술의 결과입니다. 다른 답변은 정확한 한계를 언급하지 않았으므로 게시하기로 결정했습니다.
다음에 대한 PHP의 최대 정수 값 :
- 32 비트 버전은 2147483647입니다.
- 64 비트 버전은 9223372036854775807입니다.
따라서 32 비트 CPU 또는 32 비트 OS 또는 32 비트 컴파일 버전의 PHP를 사용하고 있음을 의미합니다. 를 사용하여 찾을 수 있습니다 PHP_INT_MAX
. 는 sum
64 비트 컴퓨터에 그것을 할 경우 정확하게 계산 될 것이다.
JavaScript의 최대 정수 값은 9007199254740992 입니다. 당신이 작업 할 수있는 가장 큰 정확한 적분 값은 2 53입니다 (이 질문 에서 취함 ). 는 sum
이 제한을 초과합니다.
정수 값이이 한계를 초과하지 않으면 정상입니다. 그렇지 않으면 임의의 정밀 정수 라이브러리를 찾아야합니다.
답변
완전성에 대한 C의 답은 다음과 같습니다.
#include <stdio.h>
int main(void)
{
unsigned long long sum = 0, i;
for (i = 0; i <= 1000000000; i++) //one billion
sum += i;
printf("%llu\n", sum); //500000000500000000
return 0;
}
이 경우 핵심은 C99의 long long
데이터 유형을 사용하는 것입니다. C가 관리 할 수있는 가장 큰 기본 스토리지를 제공하며 실제로 매우 빠르게 실행됩니다 . 이 long long
유형은 대부분의 32 비트 또는 64 비트 시스템에서도 작동합니다.
한 가지주의 사항이 있습니다. Microsoft에서 제공하는 컴파일러는 14 년 된 C99 표준을 명시 적으로 지원하지 않으므로 Visual Studio에서이 기능을 실행하면 문제가 발생합니다.
답변
내 생각에 합계가 기본 용량 int
(2 31 -1 = 2,147,483,647)을 초과하면 Node.js 및 PHP가 부동 소수점 표현으로 전환되고 반올림 오류가 발생하기 시작합니다. Go와 같은 언어는 아마도 가능한 한 오랫동안 정수 형식 (예 : 64 비트 정수)을 고수하려고합니다 (실제로 시작하지 않은 경우). 답은 64 비트 정수에 적합하므로 계산이 정확합니다.
답변
펄 스크립트는 우리에게 예상되는 결과를줍니다 :
use warnings;
use strict;
my $sum = 0;
for(my $i = 0; $i <= 1_000_000_000; $i++) {
$sum += $i;
}
print $sum, "\n"; #<-- prints: 500000000500000000
답변
이에 대한 대답은 “놀랍게도”간단합니다.
대부분의 아시다시피 32 비트 정수는 −2,147,483,648 에서 2,147,483,647 입니다. PHP가 결과를 얻는다면 어떻게됩니까?
보통 2,147,483,647 + 1 이 −2,147,483,648 로 바뀌는 즉각적인 “오버 플로우”를 기대합니다 . 그러나 그렇지 않습니다. PHP가 더 큰 숫자를 만나면 INT 대신 FLOAT를 반환합니다.
PHP가 정수 유형의 범위를 벗어나는 숫자를 발견하면 대신 부동 소수점으로 해석됩니다. 또한 정수 유형의 범위를 넘어 숫자를 생성하는 연산은 대신 부동 소수점을 반환합니다.
http://php.net/manual/en/language.types.integer.php
PHP FLOAT 구현이 IEEE 754 배정 밀도 형식을 따른다는 것은 PHP가 정밀도를 잃지 않고 52 비트까지의 숫자를 처리 할 수 있다는 것을 의미합니다. (32 비트 시스템에서)
따라서 합계가 9,007,199,254,740,992 ( 2 ^ 53 인 경우 )에서 PHP 수학에서 반환되는 Float 값은 더 이상 정확하지 않습니다.
E:\PHP>php -r "$x=bindec(\"100000000000000000000000000000000000000000000000000000\"); echo number_format($x,0);"
9,007,199,254,740,992
E:\PHP>php -r "$x=bindec(\"100000000000000000000000000000000000000000000000000001\"); echo number_format($x,0);"
9,007,199,254,740,992
E:\PHP>php -r "$x=bindec(\"100000000000000000000000000000000000000000000000000010\"); echo number_format($x,0);"
9,007,199,254,740,994
이 예제는 PHP가 정밀도를 잃는 지점을 보여줍니다. 첫째, 마지막 significatn 비트가 삭제되어 처음 두 표현식은 같은 수를 얻습니다.
NOW ON부터는 기본 데이터 형식으로 작업 할 때 전체 수학이 잘못됩니다.
파이썬이나 펄과 같은 다른 해석 언어의 경우에도 같은 문제입니까?
나는 그렇게 생각하지 않습니다. 나는 이것이 유형 안전이없는 언어의 문제라고 생각합니다. 위에서 언급 한 바와 같이 정수 오버 플로우는 고정 데이터 유형을 사용하는 모든 언어에서 발생하지만, 유형 안전이없는 언어는 다른 데이터 유형에서이를 포착하려고 시도 할 수 있습니다. 그러나 일단 “자연”(시스템 제공) 경계에 도달하면 올바른 결과를 제외한 모든 것을 반환 할 수 있습니다.
그러나 이러한 시나리오에 따라 언어마다 스레딩이 다를 수 있습니다.