Почему (18446744073709551615 == -1) истинно?

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

(string::npos == ULONG_MAX)

и

(string::npos == -1)

являются истинными.

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

(18446744073709551615 == -1)

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

Как это возможно? Это из-за двоичного разговора?

Ответ 1

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- +1.

0000000000000000000000000000000000000000000000000000000000000001b

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

1111111111111111111111111111111111111111111111111111111111111110b

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

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

"Но, подождите", спросите вы, если в Twos Compliment -1 есть,

1111111111111111111111111111111111111111111111111111111111111111b

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

1111111111111111111111111111111111111111111111111111111111111111b

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

Обновить

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

Ответ 2

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 определяется реализацией (но способный удерживать размер самого большого возможного массива в рассматриваемой системе).

Ответ 3

В соответствии со стандартом С++ (номер документа: 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)

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

Ответ 4

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

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