이 for
루프가 멈추나요?
for (var i=0; 1/i > 0; i++) {
}
그렇다면 언제, 왜? 멈춘다 고 들었지만 그럴 이유가 없었습니다.
업데이트
조사의 일환으로 내부에서 일어나는 모든 일을 설명하는 매우 길고 자세한 기사를 작성했습니다. JavaScript의 숫자 유형에 대해 알아야 할 사항은 다음과 같습니다.
답변
(저는 메타 콘텐츠의 팬은 아니지만 : gotnull 과 le_m의 답변은 정확하고 유용합니다. 원래 답변 이었으며이 커뮤니티 위키가 게시 된 후 수정 된 내용 이 훨씬 더 많습니다 .이 CW의 원래 동기는 대부분의 편집의 결과로 사라졌지 만 여전히 유용합니다. 따라서 … 또한, 목록에 작성자가 몇 명 밖에 없지만 많은 다른 커뮤니티 회원들이 주석을 접고 정리하는 데 크게 도움을주었습니다. 이름에서 CW가 아닙니다.)
루프는 올바르게 구현 된 JavaScript 엔진에서 멈추지 않습니다. (엔진의 호스트 환경은 끝이 없기 때문에 결국 종료 될 수 있지만 이는 또 다른 문제입니다.)
그 이유는 다음과 같습니다.
-
때 처음
i
이며0
, 조건은1/i > 0
자바 스크립트, 때문에 사실1/0
입니다Infinity
, 그리고Infinity > 0
사실이다. -
그 후,
i
증가하고 오랫동안 양의 정수 값으로 계속 증가합니다 (추가 9,007,199,254,740,991 회 반복). 그 모든 경우에1/i
남아있을 것이다> 0
(비록에 대한 값을1/i
얻을 정말 끝을 향해 작은!) 루프는 루프에 포함까지 계속 그렇게i
값에 도달Number.MAX_SAFE_INTEGER
. -
JavaScript의 숫자는 IEEE-754 배정 밀도 이진 부동 소수점으로, 빠른 계산과 광범위한 범위를 제공하는 상당히 컴팩트 한 형식 (64 비트)입니다. 숫자를 부호 비트, 11 비트 지수 및 52 비트 유효 값으로 저장하여이를 수행합니다 (하지만 영리함을 통해 실제로 53 비트의 정밀도를 얻음). 그것은의 바이너리 유효 숫자 (플러스 약간의 영리함은) 우리에게 가치를 제공하고, 지수는 우리에게 숫자의 크기를 제공합니다 (기본 2) 부동 소수점.
당연히 중요한 비트가 너무 많아서 모든 숫자를 저장할 수있는 것은 아닙니다. 다음은 숫자 1과 형식이 저장할 수있는 1 다음으로 높은 숫자 인 1 + 2 -52 ≈ 1.00000000000000022, 그 다음으로 높은 숫자 는 1 + 2 × 2 -52 ≈ 1.00000000000000044입니다.
+ ------------------------------------------------- -------------- 부호 비트 / + ------- + ---------------------------------------- -------------- 지수 / / | + ------------------------------------------------- +-의미 / / | / | 0 01111111111 0000000000000000000000000000000000000000000000000000 = 1 0 01111111111 0000000000000000000000000000000000000000000000000001 ≈ 1.00000000000000022 0 01111111111 0000000000000000000000000000000000000000000000000010 ≈ 1.00000000000000044
1.00000000000000022에서 1.00000000000000044로 점프했습니다. 1.0000000000000003을 저장할 방법이 없습니다. : 그것도 정수로 일어날 수 있습니다
Number.MAX_SAFE_INTEGER
(9,007,199,254,740,991를) 형식으로 저장할 수있는 가장 높은 양의 정수 값i
과i + 1
모두 정확하게 표현할 수 (있는 스펙 ). 9,007,199,254,740,991과 9,007,199,254,740,992는 모두 표현할 수 있지만 다음 정수인 9,007,199,254,740,993은 표현할 수 없습니다. 9,007,199,254,740,992 이후에 나타낼 수있는 다음 정수는 9,007,199,254,740,994입니다. 다음은 비트 패턴입니다. 맨 오른쪽 (최하위) 비트에 유의하십시오.+ ------------------------------------------------- -------------- 부호 비트 / + ------- + ---------------------------------------- -------------- 지수 / / | + ------------------------------------------------- +-의미 / / | / | 0 10000110011 1111111111111111111111111111111111111111111111111111 = 9007199254740991 (Number.MAX_SAFE_INTEGER) 0 10000110100 0000000000000000000000000000000000000000000000000000 = 9007199254740992 (Number.MAX_SAFE_INTEGER + 1) x xxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 9007199254740993 (Number.MAX_SAFE_INTEGER + 2)를 저장할 수 없습니다. 0 10000110100 0000000000000000000000000000000000000000000000000001 = 9007199254740994 (Number.MAX_SAFE_INTEGER + 3)
형식은 밑이 2이고 해당 지수를 사용하면 최하위 비트가 더 이상 분수가 아닙니다. 값은 2입니다. 해제 (9,007,199,254,740,992) 또는 설정 (9,007,199,254,740,994) 일 수 있습니다. 그래서이 시점에서 우리는 정수 (정수) 척도에서도 정밀도를 잃기 시작했습니다. 우리 루프에 의미가 있습니다!
-
i = 9,007,199,254,740,992
루프를 완료 한 후 다시i++
제공i = 9,007,199,254,740,992
합니다.i
다음 정수를 저장할 수없고 계산이 반올림되기 때문에 에는 변경 사항이 없습니다 .i
우리가 변경하면 변경i += 2
되지만i++
변경할 수는 없습니다. 그래서 우리는 정상 상태에 도달했습니다.i
절대 변경되지 않고 루프가 종료되지 않습니다.
다음은 다양한 관련 계산입니다.
if (!Number.MAX_SAFE_INTEGER) {
// Browser doesn't have the Number.MAX_SAFE_INTEGER
// property; shim it. Should use Object.defineProperty
// but hey, maybe it's so old it doesn't have that either
Number.MAX_SAFE_INTEGER = 9007199254740991;
}
var i = 0;
console.log(i, 1/i, 1/i > 0); // 0, Infinity, true
i++;
console.log(i, 1/i, 1/i > 0); // 1, 1, true
// ...eventually i is incremented all the way to Number.MAX_SAFE_INTEGER
i = Number.MAX_SAFE_INTEGER;
console.log(i, 1/i, 1/i > 0); // 9007199254740991 1.1102230246251568e-16, true
i++;
console.log(i, 1/i, 1/i > 0); // 9007199254740992 1.1102230246251565e-16, true
i++;
console.log(i, 1/i, 1/i > 0); // 9007199254740992 1.1102230246251565e-16, true (no change)
console.log(i == i + 1); // true
답변
대답:
조건 1/i > 0
은 항상 true로 평가됩니다.
-
처음에는 그것 때문에 사실
1/0
들을 평가에Infinity
와Infinity > 0
사실 -
그것은
1/i > 0
모두에게 사실i < Infinity
이며i++
결코 도달하지 않기 때문에 사실로 유지됩니다Infinity
.
왜 i++
도달 하지 Infinity
않습니까? Number
데이터 유형 의 제한된 정밀도로 인해 다음과 같은 값이 있습니다 i + 1 == i
.
9007199254740992 + 1 == 9007199254740992 // true
일단 i
값 (에 대응하는 것을 도달하면 ), 그 후에도 동일하게 유지한다 .Number.MAX_SAFE_INTEGER
+ 1
i++
그러므로 우리는 무한 루프를 가지고 있습니다.
부록:
왜 9007199254740992 + 1 == 9007199254740992
?
JavaScript의 Number
데이터 유형은 실제로 64 비트 IEEE 754 배정 밀도 float 입니다. 각각 Number
은 분해되어 1 비트 부호, 11 비트 지수 및 52 비트 가수의 세 부분으로 저장됩니다. 그 값은 -1 sign × mantissa × 2 exponent 입니다.
9007199254740992 는 어떻게 표현됩니까? 로 1.0 × 2 (53) , 또는 이진 :
가수의 최하위 비트를 증가 시키면 다음으로 높은 숫자를 얻습니다.
그 숫자의 값은 1.00000000000000022… × 2 53 = 9007199254740994입니다.
그게 무슨 뜻입니까? Number
900719925474099 2 또는 900719925474099 4 중 하나 일 수 있지만 그 사이에는 없습니다.
이제 900719925474099 2 + 1 을 나타 내기 위해 어떤 것을 선택 할까요? IEEE 754 반올림 규칙은 900719925474099 : 대답 제공 2 .
답변
Number.MAX_SAFE_INTEGER
상수는 자바 스크립트의 최대 안전 정수를 나타냅니다. MAX_SAFE_INTEGER
상수의 값을 갖는다 9007199254740991
. 그 숫자 뒤에 이유는 자바 스크립트를 사용한다는 것입니다 배정 밀도 부동 소수점 형식 번호 에 지정된 IEEE 754 (2 – 만 안전하게 사이의 숫자를 나타낼 수 (53) – 1)과 2 (53) – 1이다.
이 문맥에서 안전은 정수를 정확하게 표현하고 정확하게 비교할 수있는 능력을 의미합니다. 예를 들어 는 수학적으로 잘못된로 Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2
평가됩니다 true
. 자세한 내용은를 참조하십시오 Number.isSafeInteger()
.
MAX_SAFE_INTEGER
의 정적 속성 이므로 생성 한 객체 의 속성이 아닌 Number
항상으로 사용합니다 .Number.MAX_SAFE_INTEGER
Number
최신 정보:
삭제 된 답변에 누군가 언급 : i
무한대에 도달하지 않습니다. 이 도달하면 Number.MAX_SAFE_INTEGER
, i++
더 이상 변수를 증가하지 않습니다. 사실 이것은 정확 하지 않습니다 .
@TJ 크라우 더는 댓글 i = Number.MAX_SAFE_INTEGER; i++; i == Number.MAX_SAFE_INTEGER;
입니다 false
. 그러나 다음 반복은 변하지 않는 상태에 도달하므로 main의 대답은 정확합니다.
i
예제에서 Infinity
.