Почему (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