如何使用SSE2将double/int设置为double?

mar*_*zzz 2 c++ truncate simd intrinsics sse2

float,它似乎很容易floor()和比int(),比如:

float z = floor(LOG2EF * x + 0.5f);
const int32_t n = int32_t(z);   
Run Code Online (Sandbox Code Playgroud)

成为:

__m128 z = _mm_add_ps(_mm_mul_ps(log2ef, x), half);
__m128 t = _mm_cvtepi32_ps(_mm_cvttps_epi32(z));
z = _mm_sub_ps(t, _mm_and_ps(_mm_cmplt_ps(z, t), one));

__m128i n = _mm_cvtps_epi32(z);
Run Code Online (Sandbox Code Playgroud)

但是,如果double使用SSE2,您将如何实现这一目标?

这是我要转换的双版本:

double z = floor(LOG2E * x + 0.5);
const int32_t n = int32_t(z);
Run Code Online (Sandbox Code Playgroud)

Max*_*hof 5

只需使用单精度()内在的双精度等价物(...pd...):...ps...

__m128i n = _mm_cvtpd_epi32(z);
Run Code Online (Sandbox Code Playgroud)

根据英特尔内部指南,SSE2确实可以使用该内在函数:https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=4966,1917&techs=SSE2

__m128i _mm_cvtpd_epi32 (__m128d a)

将打包的双精度(64位)浮点元素转换a为打包的32位整数,并将结果存储在dst.

FOR j := 0 to 1
  i := 32*j
  k := 64*j
  dst[i+31:i] := Convert_FP64_To_Int32(a[k+63:k])
ENDFOR
Run Code Online (Sandbox Code Playgroud)

  • 你的问题明确地想要一个`const int32_t`.这就是内在名称中的"32"代表的含义.**d**ouble**p**recision(64位浮点)由`pd`表示,而不是'ps`表示**s**ingle**p**recision.查看内在指南,它非常清楚地记录了所涉及的数据类型.为方便起见,我还在答案中添加了最相关信息的引用. (4认同)
  • @markzzz 和 Max:`cvtpd_epi32` 使用当前舍入模式。`cvttpd_epi32` 使用截断(向 0)。这些都不是“地板”(朝向 -Infinity)。因此,问题标题具有误导性,因为您实际上是在询问 `(int)floor(d + 0.5)` 被用作对 `(int)nearbyint(d)` 或 `lrint(d)` 的不良模拟。 (2认同)
  • 更重要的是,@ mark的实际问题包括将`double`舍入到最接近的整数,结果为'double`.(实际上,通过从原始部分中减去整数部分(在某些缩放之后?)来获得小数部分).但是,转换为整数和返回将起作用,因为在这种情况下,它只需要适用于小幅度的双精度.在-2 ^ 31 + 2 ^ 31-1范围之外加倍可以转换为整数值"0x80000000"(INT_MIN).但这对一般情况来说并不好,所以这个问题应该要求你真正需要的一切+细节. (2认同)