使用位运算符和加减法,我该如何检查有符号整数是否为正数(即既非负数也非零)?我相信答案很简单,但就是想不起来。
int n
提供一个“严格为正”谓词,有以下几种方法:
-n
如果n
严格为正,则符号(最高)位设置,否则在所有其他情况下均不清除,但是会出现n == INT_MIN
的情况;~n
如果n
严格为正或为0,则符号位设置,在所有其他情况下均不清除,包括n == INT_MIN
;-n & ~n
如果n严格为正,则符号位设置,在所有其他情况下均不清除。应用一个无符号移位,将其转换为0 / 1的答案:
int strictly_positive = (unsigned)(-n & ~n) >> ((sizeof(int) * CHAR_BIT) - 1);
编辑:正如评论中caf所指出的那样,-n
在n == INT_MIN
时会导致溢出(仍然假设为2的补码)。C标准允许程序在这种情况下失败(例如,您可以使用带有-ftrapv
选项的GCC启用有符号溢出陷阱)。将n
强制转换为无符号数可解决问题(无符号算术不会导致溢出)。因此,改进如下:
unsigned u = (unsigned)n;
int strictly_positive = (-u & ~u) >> ((sizeof(int) * CHAR_BIT) - 1);
n == INT_MIN
时,-n
可能会溢出。 - caf~
运算符之前将其转换为unsigned
,则它适用于所有系统。 - R.. GitHub STOP HELPING ICEint
具有与unsigned int
一样多的值位”的情况;) - cafsizeof
不是计算类型宽度的正确方法的问题……可能存在填充位。请参阅我对Jonathan答案的评论。 - Jens Gustedt如果您无法使用明显的比较运算符,那么您需要更加努力:
int i = anyValue;
if (i && !(i & (1U << (sizeof(int) * CHAR_BIT - 1))))
/* I'm almost positive it is positive */
第一个条件检查值是否为零;第二个条件检查值是否没有设置首位。这对于二进制补码、一的补码或符号数都适用。
(sizeof(int) * CHAR_BIT - 1)
次方在int中肯定是无法表示的。 - cafsizeof
进行移位计算不一定总是有效的,因为类型的宽度和大小并不强制相关。可能存在填充位。我认为像 (((unsigned)-1u)/2u)+1u)
这样的表达式会更具可移植性。尽管这也假设 signed
和 unsigned
拥有相同的宽度。 - Jens Gustedt检查最高位。0表示正数,1表示负数。
检查它不是0且最高位为0,类似这样:
int positive(int x) {
return x && (x & 0x80000000);
}