2013-09-08 3 views
1

В C++, если я делаю это:C++ Почему LLONG_MIN == -LLONG_MIN

__int64 var = LLONG_MIN; 
__int64 var2 = -var; 
cout << "var: "<< var << endl; 
cout << "var2: "<< var2 << endl; 

Выход я получаю:

var: -9223372036854775808 
var2: -9223372036854775808 

Что является частью стандарта, который покрывает это? Я предполагаю, что это целочисленное переполнение. Это было скомпилировано с использованием g ++ (GCC) 4.7.2.

У меня есть функция вычитания, и я пишу функцию добавления, и я думал, что могу просто сделать это: add(someobj &obj, long long num) { subtract(obj, -num); }. Я думаю, что это сработало бы, если бы не LLONG_MIN.

+6

Это действительно целочисленное переполнение, и артефакт (https://en.wikipedia.org/wiki/Два% 27s_complement) – syam

+1

Я думаю, что часть стандарта, которая управляет этим, наследуется от стандарта C – sehe

+0

@sehe IIRC, которое представляет собой целочисленное переполнение UB. – syam

ответ

4

Это действительно цельное переполнение и артефакт two's complement.

В вашей реализации, LLONG_MIN является -9223372036854775808, который находится в шестнадцатеричном 0x8000000000000000 (я буду использовать это шестнадцатеричное, потому что это легче увидеть, что происходит с битами).

Когда вы вычислить -LLONG_MIN в системе, которая использует два в дополнение, под капотом сначала сделать побитовое не (дающим 0x7FFFFFFFFFFFFFFF == LLONG_MAX), затем добавьте 1, который перетекает в целое число и возвращает 0x8000000000000000 == LLONG_MIN.

Обратите внимание, что целочисленное переполнение со знаком является неопределенным поведением, поэтому нет никакой гарантии, что он будет вести себя последовательно на каждой реализации C++.

+0

Спасибо. Есть ли у вас какие-либо предложения о том, как я могу реализовать добавление подписанных чисел и обертывание для переполнения с помощью определенного поведения? Я должен добавить 64-битное целое к объекту. Если он переполняется, я бы предпочел, чтобы он обернулся, а не неопределенное поведение. – user2672807

0

Хотя другой ответ правильный, я не думаю, что объяснить интуитивную сторону этого:

LLONG_MIN == -2^63  == -9223372036854775808 
LLONG_MAX == 2^63 - 1 == 9223372036854775807 

Я пытался думать, за счет использования LLONG_MIN как частный случай чего-то, но потом я вспомнил, что с C++ подписанными интегральными типами самое низкое значение всегда 1 дальше от 0, чем самое высокое значение (смешно, каждый из них, от байта до double, заканчивается -...8 и +...7!).

Итак, мне было любопытно, что произойдет, если вы должны были бы свести на нет самое низкое значение, поскольку в положительной сфере нет эквивалента; и, увы, он считается переполнением целых чисел (-9223372036854775808 == 9223372036854775808 > 9223372036854775807), и, таким образом, он переполняется на 1 (9223372036854775807 + 1), что ведет нас обратно ... -9223372036854775808!


Код:

#include <iostream> 
#include <climits> 
using namespace std; 

int main() { 
    long long l1 = LLONG_MIN; 
    long long l2 = -l1; //Equivalent to l1. 
    long long l3 = l1 - 1; 
    long long l4 = l3 + 1; 
    cout << l1 << "\n"; 
    cout << l2 << "\n"; 
    cout << l3 << "\n"; 
    cout << l4 << "\n"; 

    return 0; 
} 

Выход: [. Дополнения до двух]

-9223372036854775808 
-9223372036854775808 
9223372036854775807 
-9223372036854775808