2016-12-08 11 views
10

우리는 -2 * 4^31 + 1 = -9.223.372.036.854.775.807을 알고 있습니다.이 값은 여기에 말한 것처럼 긴 길이로 저장할 수 있습니다. What range of values can integer types store in C++.Visual Studio의 긴 long 값

#include <iostream> 

unsigned long long pow(unsigned a, unsigned b) { 
    unsigned long long p = 1; 
    for (unsigned i = 0; i < b; i++) 
     p *= a; 
    return p; 
} 

int main() 
{ 
    long long nr = -pow(4, 31) + 5 -pow(4,31); 
    std::cout << nr << std::endl; 
} 

왜 -9.223.372.036.854.775.808 대신 -9.223.372.036.854.775.803 보여 않습니다 그래서 나는이 작업을? Visual Studio 2015를 사용하고 있습니다.

+0

설명은 확장 토론이 아닙니다. 이 대화는 [채팅으로 이동되었습니다] (http://chat.stackoverflow.com/rooms/130291/discussion-on-question-by-peter-long-long-value-in-visual-studio). –

답변

14

이것은 세 가지 (!) 원인이있는 매우 불쾌한 작은 문제입니다.

먼저 부동 소수점 연산이 근사치입니다. 컴파일러가 float 또는 double을 반환하는 pow 함수를 선택하면 4 ** 31이 너무 커서 5가 1ULP (최소 정밀도 단위)보다 작으므로 추가하면 아무 것도 수행되지 않습니다 (다른 말로하면 4.0 ** 31 + 5 == 4.0 ** 31). -2로 곱하면 손실없이 수행 할 수 있으며 그 결과는 손실없이 long long에 잘못 저장 될 수 있습니다 (-9.223.372.036.854.775.808).

둘째, 표준 헤더 일 수 있습니다.은 다른 표준 헤더를 포함하지만 필수는 아닙니다. Visual Studio의 <iostream> 버전은 (전역 네임 스페이스에 pow을 선언 함)을 포함하지만 Code :: Blocks 버전에는 포함되지 않습니다.그는 형 int의 모두 인수 431을 전달하고 선언 된 함수가 유형 unsigned의 인수를 가지고 있기 때문에

셋째, 영업 이익의 pow 기능이 선택되어 있지 않습니다. C++ 11 이후에는 많은 오버로드 (또는 함수 템플릿)가 std::pow입니다. 이들 모두는 float 또는 double을 반환합니다 (인수 중 하나가 long double 유형이 아닌 경우 - 여기서는 적용되지 않음).

따라서 std::pow의 오버로드는 ...보다 좋은 일치가 될 것입니다 ... 두 개의 반환 값을 사용하면 부동 소수점 반올림이 발생합니다.

이야기의 도덕 : 정말로 당신이하고있는 것을 알지 않는 한, 표준 라이브러리 기능과 동일한 이름으로 함수를 작성하지 마십시오!

+0

"Visual Studio includes"는 Visual Studio에 포함 된 ''버전에 " –

+0

@LThode가 포함되어 있습니다."편집을 해주셔서 감사합니다. –

2

가장 낮은 long 값은 numeric_limits에서 얻을 수 있습니다. 오래 오래 들어가 있습니다 :

-9223372036854775808

호출되는 pow() 기능이없는 당신 때문에 관찰 된 결과이다 : 결과

auto lowest_ll = std::numeric_limits<long long>::lowest(); 

. 함수의 이름을 변경하십시오.

+0

질문 아래의 설명을 참조하십시오. 그는 또한 자체 제작 된 부호없는 long long function을 시도했습니다. –

3

pow(4U, 31U)을 사용하지 않는 한 은 두 인수의 변환이 필요한 반면 Visual Studio는 pow(double, int)을 정의했습니다. 단 하나의 인수 만 변환하면됩니다. C++의 오버로드 해상도는 결과 유형이 아닌 입력을 기반으로합니다.

+0

또한, 을 포함하여 * 표준 버전의'pow'을 선언 할 수도 있습니다. 문제는 그것이 VS에서 발생하고 Code : Blocks에서는 발생하지 않는다고 생각합니다. –

1

처음으로 pow을 호출하면 부동 소수점에서 작동하는 C 표준 라이브러리의 함수가 사용됩니다. 당신의 pow 기능에 고유 한 이름을 제공하십시오 :

unsigned long long my_pow(unsigned a, unsigned b) { 
    unsigned long long p = 1; 
    for (unsigned i = 0; i < b; i++) 
     p *= a; 
    return p; 
} 

int main() 
{ 
    long long nr = -my_pow(4, 31) + 5 - my_pow(4, 31); 
    std::cout << nr << std::endl; 
} 

이 코드는 오류보고 "단항 마이너스 연산자는 부호없는 형식에 적용을 아직 서명되지 않은 결과". 근본적으로 원래의 코드는 부동 소수점 함수라고 불리며, 값을 무효화하고, 정수 연산을 적용했습니다.이 값은 찾고자하는 답을 줄 수있는 충분한 정밀도를 갖지 않았습니다 (예 : 19 자리의 사전 설정에서!). 당신이 찾고있는 해답을 얻으려면,에 서명 변경 : 함수가 unsigned을 기대하고 있기 때문에 다른 답변에 명시된 바와 같이

long long my_pow(unsigned a, unsigned b); 
이 MSVC++ 2013 년 나를 위해 일한

을, 당신은 부동 소수점 pow을 받고있어 부호있는 정수를 받는다. 정수에 U을 추가하면 pow 버전이 호출됩니다.

+0

"부호없는 유형에 단항 마이너스 연산자를 적용한 경우 결과가 여전히 서명되지 않음"은 오류가 아닙니다. –

+0

MSVC로 가져 가십시오. :) 특별한 컴파일 플래그를 설정하지 않았습니다. – cyberbisson

2

-9.223.372.036.854.775.808 결과에 대한 유일한 설명은 double 값을 반환하는 표준 라이브러리의 pow 함수의 사용입니다. 이 경우 5은 이중 계산의 정밀도보다 낮을 것이며 그 결과는 정확히 -2 이며 길이가 길면 0x8000000000000000 또는 -9.223.372.036.854.775.808이됩니다.

부호없는 long long을 반환하는 함수를 사용하는 경우 부호없는 유형에 단항 빼기를 적용하고 여전히 ULL을 받는다는 경고가 표시됩니다. 따라서 전체 연산은 부호없는 long long으로 실행되어야하며 오버플로없이 0x8000000000000005을 부호없는 값으로 지정해야합니다. 부호있는 값으로 변환하면 결과는 정의되지 않지만 알고있는 모든 컴파일러는 부호가있는 정수를 같은 표현으로 사용합니다 (-9.223.372.036.854.775.803).

그러나 단지 사용하여 경고없이 오래 오래 서명로는 계산을 간단하게 만들 것입니다 :

long long nr = -1 * pow(4, 31) + 5 - pow(4,31); 

결과가 완벽하게 당 정의 있도록 또한, 당신은 여기에도 정의되지 않은 캐스트 나 오버 플로우가 unsigned long long을 제공하는 표준은 적어도 64 비트입니다.