Как переносится листинг -1 для неподписанного типа?

На днях Я столкнулся с этой конструкцией:

static_cast<size_type>(-1)

в некотором примере кода на С++, который, скорее всего, (в зависимости от того, где находится size_type), эквивалентен следующему C:

(size_t)(-1)

Как я понимаю, он работает на основе того, что представление -1 в двухпараметрической арифметике - это 11111...1, для такого количества бит, как у вас, так что это быстрый способ получить максимальное значение, которое неподписанный тип типа size_t может иметь место. Тем не менее, я понимаю, что C не гарантирует, что будет использоваться двойной набор; если реализация C использует одно дополнение, это будет на 1 меньше максимального значения, и если он использует значение знака, оно будет чуть более половины максимального значения.

Есть ли какая-то морщина, которую мне не хватает, что гарантирует, что это работает правильно, независимо от того, используется ли выражение целых чисел? Различия между C и С++ (многие удивительные вещи)?

Ответ 1

Требования к беззнаковой арифметике гарантируют, что приведение -1 к неподписанному типу приведет к наибольшему числу возможных для целевого типа. C99, §6.2.5/9: "... результат, который не может быть представлен результирующим беззнаковым целочисленным типом, уменьшается по модулю по числу, которое больше, чем наибольшее значение, которое может быть представлено результирующим типом".

Это то же самое в C и С++ (в стандарте С++ аналогичная формулировка содержится в сноске 41 - она ​​не является нормативной, но объясняет другую формулировку).

Ответ 2

Чтобы быть на "безопасной" стороне и делать это "правильно" (С++), стоит посмотреть на STL:

std::numeric_limits<size_t>::max()

Ответ 3

"Как я понимаю, он работает, основываясь на том, что представление -1 в двойной арифметике дополняет...".

Нет, это не основано на этом факте. Он основан на стандартном требовании, что записанные значения, преобразованные в N-разрядный неподписанный тип, должны приводить к значению без знака, которое "равно" оригиналу, подписанному по модулю 2 ^ N.

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

Ответ 4

Если вы хотите получить максимальное (или минимальное) значение определенного типа переносимым способом, лучше всего использовать стандартный класс numeric_limits следующим образом.

#include <limits>

size_type max = std::numeric_limits<size_type>::max()
size_type min = std::numeric_limits<size_type>::min()

Я подозреваю, что некоторые реализации этих функций могут использовать листинг, который вы описываете как оптимальный для платформы способ получить минимальный/максимальный.