program story

for 루프에서 과학적 표기법 사용

inputbox 2021. 1. 8. 08:10
반응형

for 루프에서 과학적 표기법 사용


최근에 형태의 루프가있는 코드를 발견했습니다.

for (int i = 0; i < 1e7; i++){
}

나는 1e7이 부동 소수점 유형이기 때문에 이것을 수행하는 지혜에 의문을 제기하고 i중지 조건을 평가할 때 승격시킬 것입니다. 이것이 우려의 원인이되어야합니까?


여기 방 코끼리 의 범위이다 int 작게 -32767 같은 +32767까지, 그리고 이러한 방법이보다 큰 값을 할당에 문제가 int되는 미정 .

그러나 요점은 매우 나쁜 습관 이기 때문에 실제로 당신을 염려해야합니다 . 예, 1e7은 부동 소수점 double 유형입니다.

i유형 승격 규칙으로 인해 부동 소수점으로 변환 될 것이라는 사실 은 다소 논란의 여지가 있습니다. 명백한 정수 리터럴 이 예기치 않게 잘리는 경우 실제 피해가 발생합니다 . "예를 통한 증명"을 통해 먼저 루프를 고려하십시오.

for (std::uint64_t i = std::numeric_limits<std::uint64_t>::max() - 1024; i ++< 18446744073709551615ULL; ){
    std::cout << i << "\n";
}

이것은 i예상대로 범위 의 모든 연속 값을 출력합니다 . 는 2의 64 제곱보다 1이 적다는 std::numeric_limits<std::uint64_t>::max()유의하십시오 18446744073709551615ULL. (여기서는 유형 ++<작업시 유용한 슬라이드 형 "연산자" 를 사용하고 unsigned있습니다. 많은 사람들이 생각 -->하고 ++<난독 화하는 것으로 간주 하지만 과학 프로그래밍에서는 일반적입니다. 특히 -->.)

이제 내 컴퓨터에서 double은 IEEE754 64 비트 부동 소수점입니다. (예 : 체계는 2의 거듭 제곱을 정확하게 표현하는 데 특히 좋습니다. IEEE754는 2의 거듭 제곱을 1022까지 정확하게 표현할 수 있습니다.) 따라서 18,446,744,073,709,551,616(2의 64 제곱)은 정확히 double로 표현할 수 있습니다. 그 전에 가장 가까운 표현 가능한 숫자 18,446,744,073,709,550,592는 1024보다 적습니다.

이제 루프를 다음과 같이 작성하겠습니다.

for (std::uint64_t i = std::numeric_limits<std::uint64_t>::max() - 1024; i ++< 1.8446744073709551615e19; ){
    std::cout << i << "\n";         
}

내 컴퓨터 에서 : (이미 본 숫자) 하나의 값만 출력 합니다. 이것은 그것이 부동 소수점 유형 임을 증명합니다 . 컴파일러가 리터럴을 정수 유형으로 처리하도록 허용 된 경우 두 루프의 출력은 동일합니다.i18,446,744,073,709,550,5921.8446744073709551615e19


int32 비트 이상 이라고 가정하면 작동합니다 .

그러나 정말로 지수 표기법을 사용하려면 루프 외부에 정수 상수를 정의하고 다음과 같이 적절한 캐스팅을 사용해야합니다.

const int MAX_INDEX = static_cast<int>(1.0e7);
...
for (int i = 0; i < MAX_INDEX; i++) {
    ...
}

이것을 고려하면 글을 쓰는 것이 훨씬 낫다고 말하고 싶습니다

const int  MAX_INDEX = 10000000;

또는 C ++ 14를 사용할 수 있다면

const int  MAX_INDEX = 10'000'000;

1e7유형의 리터럴 double이며 일반적으로 double52 비트 가수가있는 64 비트 IEEE 754 형식입니다. 약 10의 제 2 전원에 대응 열째의 모든 전력이 너무 double적어도 10의 정수를 나타낼 수 있어야 5 * 3 = 10 (15) , 정확하게 . int32 비트 인 경우 최대 값 int은 대략 10 3 * 3 = 10 9입니다 (Google 검색에 "2 ** 31-1"= 2 147 483 647, 즉 대략적인 추정치의 두 배).

따라서 실제로는 현재 데스크톱 시스템 이상에서 안전합니다.

그러나 C ++는 int16 비트 만 허용 하며, 예를 들어이 작은 임베디드 시스템 int에서는 정의되지 않은 동작을 가질 수 있습니다.


예를 들어 배열의 모든 요소를 ​​정확하게 반복하는 경우와 같이 정확한 정수 반복 횟수에 대해 반복하려는 의도가있는 경우 부동 소수점 값과 비교하는 것은 정확성상의 이유로 그다지 좋은 생각이 아닐 수 있습니다. 정수를 실수로 암시 적으로 캐스트하면 정수가 0으로 잘 리므로 경계를 벗어난 액세스의 실제 위험이 없으므로 루프가 짧게 중단됩니다.

이제 문제는이 효과가 실제로 언제 시작됩니까? 당신의 프로그램이 그들을 경험할 것인가? 요즘 일반적으로 사용되는 부동 소수점 표현은 IEEE 754입니다. 지수가 0 인 한 부동 소수점 값은 본질적으로 정수입니다. C 배정 밀도는 가수에 대해 52 비트를 부동화하여 정수 정밀도를 최대 2 ^ 52 (약 1e15 정도)의 값으로 제공합니다. f부동 소수점 리터럴이 단 정밀도로 해석되기를 원하는 접미사 지정하지 않으면 리터럴은 배정 밀도가되고 암시 적 변환도이를 대상으로합니다. 따라서 루프 종료 조건이 2 ^ 52 미만인 한 안정적으로 작동합니다 !

Now one question you have to think about on the x86 architecture is efficiency. The very first 80x87 FPUs came in a different package, and later a different chip and as aresult getting values into the FPU registers is a bit awkward on the x86 assembly level. Depending on what your intentions are it might make the difference in runtime for a realtime application; but that's premature optimization.

TL;DR: Is it safe to to? Most certainly yes. Will it cause trouble? It could cause numerical problems. Could it invoke undefined behavior? Depends on how you use the loop end condition, but if i is used to index an array and for some reason the array length ended up in a floating point variable always truncating toward zero it's not going to cause a logical problem. Is it a smart thing to do? Depends on the application.

ReferenceURL : https://stackoverflow.com/questions/37917806/using-scientific-notation-in-for-loops

반응형