char f1( int a,unsigned b ) { return abs(a) <= b; }
对于执行速度,我想重写如下:
char f2( int a,unsigned b ) { return (unsigned)(a+b) <= 2*b; } // redundant cast
或者使用此签名,即使对于非负b,也可能具有微妙的含义:
char f3( int a,int b ) { return (unsigned)(a+b) <= 2*b; }
这两种替代方案都可以在一个平台上进行简单的测试,但我需要便携式.假设非负b并且没有溢出风险,这是典型硬件和C编译器的有效优化吗?它对C也有效吗?
注意:作为带有-O3的gcc 4.8 x86_64上的C,f1()使用6个机器指令而f2()使用4. f3()的指令与f2()的指令相同.同样有趣的是:如果b作为文字给出,则两个函数都编译为3条指令,这些指令直接映射到f2()中指定的操作.
解决方法
char f2( int a,unsigned b );
这包含表达式
a + b
由于其中一个操作数具有signed,另一个(对应的)无符号整数类型(因此它们具有相同的“整数转换等级”),因此 – 遵循“常用算术转换”(第6.3.1.8节) – 操作数有符号整数类型转换为另一个操作数的无符号类型.
即使有问题的值无法用新类型表示,也可以很好地定义转换为无符号整数类型:
[..] if the new type is unsigned,the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type. 60
§ 6.3.1.3/2
脚注60只是说所描述的算术与数学值一起工作,而不是键入的算术.
现在,使用更新的代码
char f2_updated( int a,int b ); // called f3 in the question
事情会有所不同.但由于b被假定为非负数,并且假设INT_MAX <= UINT_MAX,您可以将b转换为无符号,而不必担心它之后具有不同的数学值.因此你可以写
char f2_updated( int a,int b ) { return f2(a,(unsigned)b); // cast unnecessary but to make it clear }
再看f2,表达式2 * b进一步限制了b的允许范围不大于UINT_MAX / 2(否则数学结果将是错误的).
所以只要你保持在这些范围内,一切都很好.
注意:无符号类型不会溢出,它们会根据模运算“换行”.
N1570的报价(C11工作草案)
最后一句话:
IMO是编写此函数的唯一合理选择
#include <stdbool.h> #include <assert.h> bool abs_bounded(int value,unsigned bound) { assert(bound <= (UINT_MAX / 2)); /* NOTE: Casting to unsigned makes the implicit conversion that otherwise would happen explicit. */ return ((unsigned)value + bound) <= (2 * bound); }
对绑定使用带符号的类型没有多大意义,因为值的绝对值不能小于负数. abs_bounded(value,something_negative)总是假的.如果有可能出现负面界限,那么我会在这个函数之外捕获它(否则它会“太多”),如:
int some_bound; // ... if ((some_bound >= 0) && abs_bounded(my_value,some_bound)) { // yeeeha }