阈值绝对值

2024-05-01

我有以下功能:

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++ 也有效吗?


注意:作为 gcc 4.8 x86_64 上的 C++-O3, f1()使用 6 条机器指令f2()4.使用说明f3()与那些相同f2()。同样感兴趣的是:如果b作为文字给出,两个函数都编译为 3 条指令,直接映射到中指定的操作f2().


从带有签名的原始代码开始

char f2( int a, unsigned b );

这包含表达式

a + b

由于这些操作数之一具有有符号整数类型,另一个具有(相应的)无符号整数类型(因此它们具有相同的“整数转换等级”),那么 - 遵循“常规算术转换”(第 6.3.1.8 节) - 操作数为有符号整数类型转换为另一个操作数的无符号类型。

到无符号整数类型的转换是明确定义的,即使所讨论的值不能用新类型表示:

[..] 如果新类型是无符号的,则通过在新类型可以表示的最大值上重复加或减 1 来转换该值,直到该值在新类型的范围内。 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 to an unsigned不用担心它之后会有不同的数学值。因此你可以写

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);
}

使用有符号类型bound没有多大意义,因为值的绝对值不能小于负数。abs_bounded(value, something_negative)总是错误的。如果存在负界的可能性,那么我会在该函数之外捕获它(否则它会“太多”),例如:

int some_bound;
// ...
if ((some_bound >= 0) && abs_bounded(my_value, some_bound)) {
  // yeeeha
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

阈值绝对值 的相关文章

随机推荐