Verifying Mathematical Calculations

Panther·2021년 8월 15일
0

https://developer.apple.com/documentation/uikit/app_and_environment/updating_your_app_from_32-bit_to_64-bit_architecture/verifying_mathematical_calculations

"Ensure the accuracy of your math operations in 64-bit architecture."

64비트 아키텍처에서 수리적 연산 작업의 정확성을 확실하도록 합니다.

Overview

수리적 연산 작업은 64비트 런타임에서 숫자에 의해 영향을 받습니다. 앱이 수행하는 모든 연산 결과의 정확성을 리뷰하시기 바랍니다. 부호가 있는 결과 값이 작업 및 피연산자에서 정확한지 확실히 하기 위해 부호가 있는 결과 값을 확인하시기 바랍니다. 비트 마스크 코드가 타입 크기에 대한 가정을 만들고 있지 않다는 것을 검증하시기 바랍니다.

Verify the Accuracy of Signed Math Operations

C 및 C와 유사한 언어는 값이 큰 넓이의 변수에 할당될 때, 인티저에서 상위 비트를 부호 비트로 다룰 것인지를 결정하는 부호 확장 규칙의 집합을 사용합니다. 부호 확장 규칙은 아래와 같습니다.

  1. 부호가 없는 값은 더 큰 타입으로 승격될 때 확장되는 0(부호 확장되지 않은)입니다.
  2. 부호가 있는 값은 더 큰 타입으로 승역될 때 항상 확장되는 부호이며, 결과 타입이 부호가 없더라도 그렇습니다.
  3. 상수(0x8L처럼 suffix에 의해 수정되지 않는 한)는 값을 갖고 있는 가장 작은 크기처럼 다뤄집니다. 16진수는 컴파일러에 의해 int, long, long long 타입으로써 다뤄질 것이며, 부호의 여부에 관계가 없습니다.
  4. 같은 크기를 갖는 부호가 있는 값 및 부호가 없는 값의 합계는 부호가 없는 값입니다.
int a = -2;
unsigned int b = 1;
long c = a + b;
long long d = c; // To get a consistent size for printing.

printf("%lld\n", d);

이 코드가 32비트 런타임에서 실행될 때 결과는 -1(0xffffffff)입니다. 64비트 런타임에서 동작하는 같은 코드의 경우 결과는 4294967295(0x00000000ffffffff)로 부정확한 값입니다.

이와 같은 일이 왜 발생하는지 이해하려면 부호가 있는 값과 부호가 없는 값의 합이 부호가 없는 값의 결과를 갖는다는 것을 생각해보시기 바랍니다(규칙 4). 이 결과는 더 큰 타입으로 프롬프트되지만, 이 승격은 부호 확장을 야기시키지는 않습니다.

이 문제를 해결하려면 b를 긴 인티저에 캐스팅해야 합니다. 이 캐스팅은 부호가 없는 확장된 승격의 b를 64비트 타입으로 강제하며, 더하기 전에 수행됩니다. 그러므로 부호가 있는 인티저(부호가 있는 것과 같은 환경에서)로 강제하면서 수행됩니다. 이와 같은 변경사항으로, 결과는 예상된 -1입니다.

unsigned short a = 1;
unsigned long b = (a << 31);
unsigned long long c = b;

printf("%llx\n", c);

위 코드에서 비트 시프팅은 C로 복사되는 B에서 다른 위치의 값을 이동시키기 위해 사용됩니다. printf(32비트 실행의 결과)의 예상되는 결과는 0x80000000입니다. 그러나 64비트 실행 결과는 0xffffffff80000000입니다.

이와 같은 결과를 보이는 이유는 두 가지가 있습니다. 첫 번째는 왼쪽 시프트 오퍼레이터 <<가 호출될 때, 변수 a는 타입 int로 프롬프트됩니다. 모든 짧은 인티저 값은 부호가 있는 int 타입에 적합할 수 있기 때문에, 이 승격의 결과는 부호가 있습니다. 두 번째는, 왼쪽 시프팅이 완료될 때, 결과는 긴 인티저에 저장된다는 점입니다. 그러므로 (a << 31)로 나타나는 부호 있는 32비트 값이 64비트 값으로 올라갈 때(결과 타입이 부호가 없을지라도), 해당 값은 확장된 부호(규칙 2)입니다.

해결방법은 시프팅 전에 초기값을 긴 인티저로 캐스팅하는 것입니다. 짧은 인티저는 한 번만 승격됩니다(이번의 경우 64비트 타임, 64비트 실행행 가능한 것으로 컴파일될 때).

Check Your Code for Assumptions About Type Size

64비트 값을 갖는 비트 마스크로 작업할 떄, 실수로 32비트 값을 가져오지 않기 위해 아래 팁을 따르시기 바랍니다.

데이터 타입이 특정 길이를 갖는다고 가정하지 않아야 합니다. 긴 인티저 타입의 변수에 저장된 비트를 통해 시프팅 하는 경우 비트의 수를 결정하기 위해 LONG_BIT를 사용하시기 바랍니다. 변수의 길이를 초과하는 시프트의 결과는 아키텍처에 따라 달라집니다.

필요한 경우 반전된 비트 마스트를 사용하시기 바랍니다. 긴 인티저로 비트 마스크를 사용할 때 조심해야 합니다. 왜냐하면 32비트와 64비트 런타임의 넓이는 다르기 때문입니다. 0 확장 혹은 1 확장에 따라 달라질 수 있도록 비트 마스크 생성에는 두 가지 방법이 있습니다.

  • 64비트 런타임에서 상위 32비트에 0을 포함하는 비트 마스크를 원하는 경우 보통의 고정된 넓이 비트 마스크는 기대한 것처럼 작동할 것입니다. 왜냐하면 부호가 없는 방식에서 64비트 양으로 확장되기 때문입니다.
  • 상위 비트에서 1을 포함하는 비트 마스크 값을 원한다면, 비트 마스크를 역의 bitwise 역으로 쓰시기 바랍니다.
function_name(long value)
{
    // Use the complement (~) operator to get ones instead of zeros.
    // Mask will be 0xfffffffc in the 32-bit runtime,
    //   or 0xfffffffffffffffc in the 64-bit runtime.
    long mask = ~0x3;
    return (value & mask);
}

64비트 런타임에서 비트 마스크에 있는 상위 비트는 항상 1이라는 것을 기억하시기 바랍니다.

See Also


Performance and Accuracy

Optimizing Memory Performance

앱의 메모리 사용에 미치는 64비트 런타임의 영향을 측정합니다.

https://developer.apple.com/documentation/uikit/app_and_environment/updating_your_app_from_32-bit_to_64-bit_architecture/optimizing_memory_performance
https://velog.io/@panther222128/Optimizing-Memory-Performance


0개의 댓글