Почему numeric_limits :: max () не равно -1? - PullRequest
3 голосов
/ 15 апреля 2020
#include <iostream>
#include <cstdint>

using namespace std;

static_assert(-1 == numeric_limits<uint64_t>::max()); // ok
static_assert(-1 == numeric_limits<uint32_t>::max()); // ok
static_assert(-1 == numeric_limits<uint16_t>::max()); // error

int main()
{
    cout << numeric_limits<uint16_t>::max() << endl;
    cout << uint16_t(-1) << endl;
}

вывод:

65535
65535

Почему numeric_limits<uint16_t>::max() не равно -1?

Обновление:

Согласно cppref :

Аналогично USHRT_MAX не может быть беззнакового типа: его тип может быть int.

Ответы [ 2 ]

5 голосов
/ 15 апреля 2020

Поскольку -1 не преобразуется в uint16_t.

std::numeric_limits<std::uint16_t>::max() повышается до int, а -1 != 65535.

4 голосов
/ 15 апреля 2020

Целочисленные конверсии и продвижения

Значение uint16_t проходит через целочисленное продвижение , тогда как для (вашей конкретной платформы; см. Ниже) случаи uint32_t и uint64_t имеют значение -1 значение (само по себе не целочисленный литерал, а унарный минус, примененный к целочисленному литералу 1), проходит через целочисленное преобразование с результирующим значением, равным максимальное соответствующее значение типов uint32_t и uint64_t из-за целочисленной конгруэнции между исходным и целевым значениями этого преобразования.

static_assert(-1 == std::numeric_limits<std::uint64_t>::max());
//            ^^
//            | Integer conversion:
//            |   Destination type: uint64_t
//            |   Resulting value: std::numeric_limits<std::uint64_t>::max()

static_assert(-1 == std::numeric_limits<std::uint32_t>::max());
//            ^^
//            | Integer conversion:
//            |   Destination type: uint32_t
//            |   Resulting value: std::numeric_limits<std::uint32_t>::max()

static_assert(-1 == std::numeric_limits<std::uint16_t>::max());
//                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//                  | Integer promotion:
//                  |   Destination type: int
//                  |   Resulting value: std::numeric_limits<std::uint16_t>::max()

С [конв. целое] / 1 и [conv.integral] / 2 :

[conv.integral] / 1

Prvalue целочисленного типа может быть преобразовано в значение другого целочисленного типа. Значение типа нумерации с незаданной областью может быть преобразовано в значение типа целого числа.

[conv.integral] / 2

Если тип назначения не имеет знака, полученное значение будет наименьшим целое число без знака соответствует исходному целому числу (по модулю 2n, где n - количество битов, используемых для представления типа без знака). [Примечание: в представлении дополнения до двух это преобразование является концептуальным, и в битовой комбинации нет изменений (если нет усечения). - конец примечания]

Одно это должно привести к одинаковому поведению для всех ваших трех примеров. Однако для случая uint16_t отличается то, что применяется [conv.integral] / 5 :

[conv.integral] / 5

преобразования, разрешенные как интегральные продвижения, исключаются из набора интегральных преобразований.

С [conv.rank] / 1

[conv.rank ] / 1

Каждый целочисленный тип имеет ранг целочисленного преобразования, определяемый следующим образом:

[...]

(1.3) Ранг long long int должен быть больше чем ранг long int, который должен быть больше ранг int, который должен превышать ранг short int, который должен превышать ранг signed char.

(1.4) Ранг любого целого типа без знака должен равняться рангу соответствующего целочисленного типа со знаком.

ранг целочисленного преобразования uint16_t (такой же ранг или ниже, чем short int) ниже, чем int, что означает, что [conv.prom] / 1 применяется для uint16_t [ выделение шахта]:

* 10 82 * [conv.prom] / 1

Prvalue целочисленного типа, отличного от bool, char16_­t, char32_­t или wchar_­t , чей ранг целочисленного преобразования меньше, чем ранг int может быть преобразован в prvalue типа int, если int может представлять все значения типа источника; в противном случае исходное значение может быть преобразовано в значение типа unsigned int.


Поведение, зависящее от платформы

Однако , хотя мы смогли аргумент для uint16_t выше из-за требования нижней границы для максимального значения unsigned short int - гарантируя, что uint16_t всегда имеет более низкий целочисленный рейтинг конверсии, чем int - мы не можем приведите обратный аргумент для того факта, что uint32_t не будет никогда не будет иметь ниже целочисленного ранга преобразования , чем int, так как стандарт ISO C ++ не устанавливает верхних границ на максимуме значение требование к основным целочисленным типам.

С [basi c .fundamental] / 2 и [basi c .fundamental] / 3 [извлечение, выделение мое]:

[basi c .fundamental] / 2

Существует пять стандартных целочисленных типов со знаком: «signed char »,« short int »,« int »,« long int »и« long long int ». В этом списке каждый тип обеспечивает как минимум столько же памяти, сколько предшествует ему в списке . [...] Простые int s имеют естественный размер, предложенный архитектурой среды исполнения ; другие целочисленные типы со знаком предоставляются для удовлетворения особых потребностей.

[basi c .fundamental] / 3

Для каждого из стандартных целочисленных типов со знаком существует соответствующий (но различный) стандартный целочисленный тип без знака: «unsigned char», «* 1145» * »,« unsigned int »,« unsigned long int »и« unsigned long long int », каждый из которых занимает тот же объем памяти и имеет те же требования к выравниванию, что и соответствующий целочисленный тип со знаком; [...]

Целочисленные типы со знаком и без знака должны удовлетворять ограничениям, указанным в стандарте C, раздел 5.2.4.2.1 .

И из C11 Стандартного черновика [извлечение, выделение шахта]:

5.2.4.2.1 Размеры целочисленных типов <limits.h>

[...] Их определяемые реализацией значения должны быть равными или большими по величине (абсолютное значение) показанным с тем же знаком.

[... ]

  • максимальное значение для объекта типа short int: SHRT_MAX +32767

  • максимальное значение для объекта типа int: INT_MAX +32767

[...]

Обратите внимание, что эти максимальные значения описывают нижнюю границу максимума значения , которые должен хранить соответствующий основной целочисленный тип, тогда как в верхней границе этих максимальных значений нет требования. Кроме того, напомним из цитаты [basi c .fundamental] / 2 выше, что каждому последующему фундаментальному (подписанному) целочисленному типу требуется только по крайней мере столько же памяти, сколько и тому, который его обрабатывает (в списке) ,

Это означает, что теоретически платформа может реализовывать short int и int как 32-битные и 64-битные целые числа соответственно, а это означает, что на этой платформе uint32_t будет иметь одно и то же целое число конверсионный ранг как (unsigned) short int, что будет означать более низкий конверсионный ранг, чем int, и в этом случае [conv.prom]/1 будет применяться также для uint32_t примера для этого конкретная платформа .

...