如何实现快速逆平方根没有未定义的行为
How to implement fast inverse sqrt without undefined behavior?
根据我对严格混叠规则的理解,这段快速平方根反比的代码将导致c++中未定义的行为:
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // type punning
i = 0x5f3759df - ( i >> 1 );
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) );
return y;
}
这段代码真的会导致UB吗?如果是,如何以符合标准的方式重新实现?如果不是,为什么不呢?
假设:在调用此函数之前,我们以某种方式检查了浮点数是IEEE 754 32位格式,sizeof(long)==sizeof(float)
和平台是little-endian。
标准的兼容方式是std::memcpy
。在您指定的假设下,这应该足够符合标准。任何合理的编译器都会把它转换成一堆寄存器移动,如果可能的话。此外,我们还可以使用c++ 11的static_assert
和<cstdint>
的固定宽度整数类型来减轻(或至少检查)您所做的一些假设。无论如何,端序是无关的,因为我们这里没有处理任何数组,如果整数类型是小端序的,浮点类型也是。
float Q_rsqrt( float number )
{
static_assert(std::numeric_limits<float>::is_iec559,
"fast inverse square root requires IEEE-comliant 'float'");
static_assert(sizeof(float)==sizeof(std::uint32_t),
"fast inverse square root requires 'float' to be 32-bit");
float x2 = number * 0.5F, y = number;
std::uint32_t i;
std::memcpy(&i, &y, sizeof(float));
i = 0x5f3759df - ( i >> 1 );
std::memcpy(&y, &i, sizeof(float));
return y * ( 1.5F - ( x2 * y * y ) );
}
您应该使用memcpy
。据我所知,这是唯一符合标准的方式,编译器足够聪明,可以用一个单词move指令替换调用。
我不认为您可以在没有严格标准意义上的UB的情况下做到这一点,仅仅是因为它依赖于float
和long
的特定值表示。标准没有指定这些(故意的),并且给予实现自由去做他们认为在这方面合适的事情,特别地将"UB"标签应用于所有依赖于这些事情的行为。
这并不意味着这将是一种"海森堡虫"或"鼻恶魔"类型的UB;最有可能的是,实现将为这种行为提供定义。只要这些定义满足代码的先决条件,就没问题。这里的前提条件是:
- 实现允许类型双关语。
-
sizeof(float) == sizeof(long)
. -
float
的内部表示是这样的,如果这些比特被解释为long
,0x5f3759df
和>> 1
就具有这种破解功能所需的含义。
然而,以一种完全可移植的方式重写它是不可能的(即没有UB) -例如,对于使用不同浮点运算实现的平台,你会怎么做?只要你依赖于特定平台的细节,你就依赖于标准中未定义但由平台定义的行为。
不。该算法首先假设IEEE754布局,这不是一个有效的标准兼容假设。特定的常数0x5f3759df
对于任何其他布局来说都是完全错误的。甚至存在这样一个常数的假设也是有缺陷的。我认为只要你颠倒浮点数内的字段顺序,它就会失效。
我也不确定Q_rsqrt(-0.0f)
是否正确工作,即使假设IEEE754。两个零(正负)应该相等,sqrt(x)
只有在x<-0.0f
- 编译C++时未定义的引用
- vscode g++链路故障:体系结构x86_64的未定义符号
- 如何修复此错误:未定义对"距离(浮点数,浮点数,浮点数,浮点数,浮点数)"的引用
- 我的项目不会像"undefined reference to `grpc::g_core_codegen_interface'"那样使用未定义的引用错误进行编译
- 不知道某个东西是否被忽略会引入未定义的行为吗
- 对C宏的未定义引用,但在定义它时会出现重新定义错误
- 未定义的引用在哪里
- 编译时的 CImg 库返回对"__imp_SetDIBitsToDevice"的未定义引用
- 对Py_Initialize()的未定义引用
- c++11评估顺序(未定义的行为)
- 使用mysql c++连接器的未定义引用
- 从python调用openMP共享库时,未定义opnMP函数
- 在 Mac 上使用 CMAKE 将 FFTW 和 FFTWPP 链接到项目中时未定义的符号
- Cmake 链接问题:未定义对 Button::mousePressEvent(QGraphicsSceneMouseE
- 未定义的引用 .. 使用 OpenCV 编译 C++ 代码时,从命令行
- 具有外部"c"和程序集的未定义函数
- 此增量后语句是否会导致未定义的行为?
- 尝试调用 .h 文件中定义的变量时出现变量未定义错误
- 在C++中使用内联方法时出现未定义的符号错误
- 如何实现快速逆平方根没有未定义的行为