鉴于此代码:(https://godbolt.org/z/qGneEne7x)
#include <stdio.h>
#include <xmmintrin.h>
int main ()
{
double d[2] = { 161785254.0, -215713672.0 };
int i[4];
unsigned u[4];
__m128d vd = _mm_loadu_pd(d);
__m128i vi = _mm_cvtpd_epi32(vd);
_mm_storeu_si128((void *)i, vi);
_mm_storeu_si128((void *)u, vi);
printf("Doubles to convert : %.1f %.1f\n", d[0], d[1]);
printf("\n");
printf("SIMD double to signed int : %i %i\n", i[0], i[1]);
printf("SIMD double stored to unsigned: %u %u\n", u[0], u[1]);
printf("\n");
printf("Scalar double to signed int : %i %i\n", (int)d[0], (int)d[1]);
printf("Scalar double to unsigned : %u %u\n", (unsigned)d[0], (unsigned)d[1]);
}
为什么优化会改变标量代码中双精度到无符号转换的结果?
这就是用 -O0 打印出来的内容:
Doubles to convert : 161785254.0 -215713672.0
SIMD double to signed int : 161785254 -215713672
SIMD double stored to unsigned: 161785254 4079253624
Scalar double to signed int : 161785254 -215713672
Scalar double to unsigned : 161785254 4079253624
一切都如预期。
打开-O1,无符号标量变为零:
Scalar double to unsigned : 161785254 0
这是为什么?
由于编译器和优化设置的工作方式,优化可能会更改 double 到 unsigned int 转换的结果。当执行从 double 到 unsigned int 的转换时,实际上是截断 double 的小数部分并将剩余的整数部分转换为无符号整数。此转换的结果取决于 double 的值以及 C 或 C++ 标准定义的规则。
但是,当应用优化时,编译器可能会做出某些假设或执行可能影响结果的转换。优化可能改变结果的一些原因包括:
编译器特定的行为:不同的编译器可能有不同的优化策略,并且在优化代码时可能不会严格遵循相同的规则,尤其是在非标准转换方面。
浮点精度:浮点数(如双精度)的精度有限。应用优化时,编译器可能会决定以更高的精度执行某些操作以避免精度损失,或者可能会进行其他影响浮点值的中间表示的转换。
表达式求值:编译器可能在优化期间重新排序或组合表达式,这可能会改变运算顺序,并导致从 double 转换为 unsigned int 时得到不同的结果。
未定义的行为:如果 double 的值超出了 unsigned int 的可表示范围,则根据 C 和 C++ 标准,该行为是未定义的。在这种情况下,优化可能会产生意想不到的结果,甚至导致崩溃。
为了避免从 double 转换为 unsigned int 时出现意外行为,必须确保 double 的值在 unsigned int 的范围内,并以不依赖于特定编译器优化或行为的方式编写代码。您还可以使用显式类型转换来使编译器清楚您的意图,但您仍然应该意识到与精度和范围相关的潜在问题。