#define ISUNSIGNED(a) (a >= 0 && ~a >= 0)
Для положительного значения со знаком, a >= 0
будет истинным (очевидно), а ~a >= 0
будет ложным, поскольку мы перевернули биты, так что теперь бит знака установлен, что приводит к отрицательному значению. Таким образом, все выражение ложно.
Для отрицательного значения со знаком, a >= 0
будет ложным (очевидно), а остальная часть выражения не будет оцениваться; общий результат для выражения ложен.
Для значения без знака a >= 0
будет всегда истинным (очевидно, поскольку значения без знака не могут быть отрицательными). Если мы перевернем биты, то ~a >= 0
также будет истинным, поскольку даже если самый старший бит (знаковый бит) установлен в 1, он все равно будет рассматриваться как положительное значение.
Таким образом, выражение возвращает true, если исходное значение и его побитовая инверсия оба положительны, то есть это значение без знака.
#define ISUNSIGNED(type) ((type)0 - 1 > 0)
Это следует вызывать с типом, а не со значением: ISUNSIGNED(int)
или ISUNSIGNED(unsigned int)
, например.
Для int
код расширяется до
((int)0 - 1 > 0)
, что неверно, поскольку -1
не больше 0
.
Для unsigned int
код расширяется до
((unsigned int)0 - 1 > 0)
Литералы со знаком 1
и 0
в выражении переводятся в unsigned
, чтобы соответствовать первому 0
, поэтому все выражение оценивается как сравнение без знака. 0 - 1
в арифметике без знака будет оборачиваться, что приведет к наибольшему возможному значению без знака (все биты установлены в 1), которое больше 0, поэтому результат равен true.
Относительно того, почему он будет работать с K & R C, но не с ANSI C, может быть, эта статья может пролить некоторый свет:
Когда неподписанный символ или неподписанный шорт расширены, тип результата
int, если int достаточно велико, чтобы представлять все значения
меньший тип. В противном случае тип результата - беззнаковое целое. Значение
правило сохранения дает наименее неожиданный арифметический результат для большинства
выражения.
Полагаю, это означает, что при сравнении, например, unsigned short
с 0
значение без знака преобразуется в signed int
, что нарушает поведение макроса.
Вероятно, вы можете обойти это, имея (a-a)
, который оценивает либо ноль со знаком, либо без знака, в зависимости от ситуации, вместо литерала 0
, который всегда подписан.