阈值是绝对值

nob*_*bar 6 c c++ micro-optimization undefined-behavior language-lawyer

我有以下功能:

char f1( int a, unsigned b ) { return abs(a) <= b; }
Run Code Online (Sandbox Code Playgroud)

对于执行速度,我想重写如下:

char f2( int a, unsigned b ) { return (unsigned)(a+b) <= 2*b; } // redundant cast
Run Code Online (Sandbox Code Playgroud)

或者使用此签名,即使对于非负面也可能具有微妙含义b:

char f3( int a, int b )      { return (unsigned)(a+b) <= 2*b; }
Run Code Online (Sandbox Code Playgroud)

这两种替代方案都可以在一个平台上进行简单的测试,但我需要便携式.假设非负b且没有溢出风险,这是典型硬件和C编译器的有效优化吗?它对C++也有效吗?


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

Dan*_*our 3

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

\n\n
char f2( int a, unsigned b );\n
Run Code Online (Sandbox Code Playgroud)\n\n

这包含表达式

\n\n
a + b\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

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

\n\n
\n

[..] 如果新类型是无符号的,则通过在新类型可以表示的最大值上重复加或减 1 来转换该值,直到该值在新类型的范围内。60

\n\n

\xc2\xa7 6.3.1.3/2

\n
\n\n

脚注 60 只是说所描述的算术适用于数学值,而不是键入的值。

\n\n

现在,使用更新后的代码

\n\n
char f2_updated( int a, int b ); // called f3 in the question\n
Run Code Online (Sandbox Code Playgroud)\n\n

事情看起来会有所不同。但由于b假定为非负,并且假设INT_MAX <= UINT_MAX您可以转换b为 anunsigned而不必担心它之后会具有不同的数学值。因此你可以写

\n\n
char f2_updated( int a, int b ) {\n  return f2(a, (unsigned)b); // cast unnecessary but to make it clear\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

再看一下f2表达式,2*b进一步限制了 的允许范围b不大于UINT_MAX/2(否则数学结果将是错误的)。\n因此,只要保持在这些范围内,一切都很好。

\n\n

注意:无符号类型不会溢出,它们根据模算术“换行”。

\n\n

引自 N1570(C11 工作草案)

\n\n
\n\n

最后一句话:

\n\n

IMO 编写此函数的唯一真正合理的选择是

\n\n
#include <stdbool.h>\n#include <assert.h>\nbool abs_bounded(int value, unsigned bound) {\n  assert(bound <= (UINT_MAX / 2));\n  /* NOTE: Casting to unsigned makes the implicit conversion that\n           otherwise would happen explicit. */\n  return ((unsigned)value + bound) <= (2 * bound);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
int some_bound;\n// ...\nif ((some_bound >= 0) && abs_bounded(my_value, some_bound)) {\n  // yeeeha\n}\n
Run Code Online (Sandbox Code Playgroud)\n

  • @EOF 我倾向于不同意:“ - 任何无符号整数类型的排名应等于相应的有符号整数类型(如果有)的排名。” 同等等级意味着转换为无符号。 (5认同)
  • Arr ...当我写答案时,编辑将 b 类型更改为有符号整数。 (2认同)

归档时间:

查看次数:

341 次

最近记录:

9 年,4 月 前