Вопрос по signed, equality, unsigned, integer-overflow, c++ – Почему (18446744073709551615 == -1) верно?

12

Когда я работал надstring::npos Я заметил что-то, и я не мог найти никакого объяснения этому в сети.

(string::npos == ULONG_MAX)

а также

(string::npos == -1)

это правда.

Итак, я попробовал это:

(18446744073709551615 == -1)

что тоже верно.

Как это может быть возможно? Это из-за бинарного разговора?

переполнение: p вы сравниваете значение без знака и со знаком Stargateur
@ davmac Я не думаю, что это правда, я думаю, что результат беззнакового переполнения определен. Вы можете получить это?stackoverflow.com/questions/5416414/... Evan Carroll
@ Bahadır проверьте мой ответ для получения дополнительной информации =) Evan Carroll
18446744073709551615 = 2 ^ 64 -1 ... жуткое совпадение? lelloman

Ваш Ответ

4   ответа
6
18,446,744,073,709,551,615

18,446,744,073,709,551,615, на самом деле2^64 − 1, Здесь важно то, что2^64-1 по существу 0 на основе2^64, Первая цифра целого числа без знака0не1, Так что, если максимальное значение1имеет два возможных значения:0, или же1 (2).

Давайте посмотрим на2^64 - 1 в 64-битном двоичном коде все биты включены.

1111111111111111111111111111111111111111111111111111111111111111b
-1

Давайте посмотрим на+1 в двоичном 64-битном

0000000000000000000000000000000000000000000000000000000000000001b

Чтобы сделать его отрицательным вКомплимент (OCP) мы инвертируем биты.

1111111111111111111111111111111111111111111111111111111111111110b

Компьютеры редко используют OCP, они используютКомплимент двоим (TCP). Чтобы получить TCP, вы добавляете его в OCP.

1111111111111111111111111111111111111111111111111111111111111110b (-1 in OCP)
+                                                              1b (1)
-----------------------------------------------------------------
1111111111111111111111111111111111111111111111111111111111111111b (-1 in TCP)

«Но, подождите», спросите вы, если в комплименте Twos-1 является,

1111111111111111111111111111111111111111111111111111111111111111b

И, если в двоичном2^64 - 1 является

1111111111111111111111111111111111111111111111111111111111111111b

Тогда они равны! И это то, что вы видите. Вы сравниваете 64-разрядное целое число со знаком с 64-разрядным целым числом без знака. В C ++ это означает преобразование значения со знаком в unsigned, что делает компилятор.

Обновить

Для технической коррекцииспасибо Давмаку в комментарияхПреобразование из-1 которыйsigned дляunsigned Тип одинакового размера фактически указывается на языке, а не является функцией архитектуры. Тем не менее, вы можете найти ответ выше полезным для понимания арки / языков, которые поддерживают комплимент двух, но не имеют спецификации, чтобы гарантировать результаты, от которых вы можете зависеть.

@davmac THX 4 цитата Red.Wave
Строго говоря, битовые представления двух чисел до их преобразования не имеют значения. Даже с дополнением 1 или знаковым представлением величины преобразование (подписанного) -1 вunsigned long всегда приведет кULONG_MAX, (Битовый шаблон будет таким жепосле конверсия конечно). davmac
@ davmac как получилось? ULONG_MAX == 2 ^ 32-1, а ~ 1 == 2 ^ 32 -2. В одной системе дополнения -1 == ~ 1 == (ULONG_MAX-1). Red.Wave
@ Red.Wave или, поскольку этот вопрос помечен C ++, а не C, см.stackoverflow.com/questions/2711522/... davmac
0

что отрицательные числа хранятся как дополнение к 2s. Это означает, что для получения абсолютного значения отрицательного числа вы инвертируете все биты и добавляете один. Это означает, что при выполнении 8-битного сравнения 255 и -1 имеют одинаковое двоичное значение 11111111. То же самое относится к большим целым числам

https://en.m.wikipedia.org/wiki/Two%27s_complement

Это не зависит от архитектуры процессора, так как это явно определяется стандартом C ++. rubenvb
Это зависит от архитектуры процессора, но большинство современных архитектур используют дополнение 2s doron
Отрицательные числа не сохраняются как 2-е дополнение по определению / Стандарт. Они ведут себя только так. rubenvb
Здесь нет подписанного переполнения. Baum mit Augen
1

N3337 или номер документа: N4296)std::string::npos определяется следующим образом

static const size_type npos = -1;

где std :: string :: size_type - это некоторый целочисленный тип без знака. Так что нет ничего удивительного в том, что std :: string :: npos равно -1. Инициализатор преобразуется в типstd::string::npos.

Что касается этого уравнения

(string::npos == ULONG_MAX) is true,

тогда это означает, что типstd::string::npos имеет тип в используемой реализацииunsigned long, Этот тип обычно соответствует типуsize_t.

В этом уравнении

(18446744073709551615 == -1)

Левый литерал имеет некоторый беззнаковый целочисленный тип, который подходит для хранения такого большого литерала. Таким образом, правый операнд также преобразуется в этот тип без знака, распространяя бит знака. Поскольку левый операнд представляет собой максимальное значение типа, они равны.

Согласно стандарту языка C ++ (по крайней мере, C ++ 11 согласноopen-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf раздел 2.14.2) левый литерал должен иметьподписанный тип, а ненеподписанный тип. Компиляторы, которые переводят его в тип без знака в тех случаях, когда нет подходящего типа со знаком, по-видимому, делают это как расширение языка. davmac
8

string::npos определяется какconstexpr static std::string::size_type string::npos = -1; (или если он определен внутри определения класса, который будетconstexpr static size_type npos = -1; но это действительно неактуально).

Обтекание отрицательных чисел, преобразованных в неподписанные типы (std::string::size_type в основномstd::size_t, который не подписан) совершенно четко определен стандартом.-1 переносится на наибольшее представимое значение беззнакового типа, которое в вашем случае18446744073709551615, Обратите внимание, что точное значение определяется реализацией, потому что размерstd::size_t определяется реализацией (но может содержать размер максимально возможного массива в рассматриваемой системе).

Похожие вопросы