【问题标题】:Are there are ARM Neon instructions for round function?是否有用于圆形功能的 ARM Neon 指令?
【发布时间】:2021-12-13 17:29:00
【问题描述】:

我正在尝试使用 ARM Neon 内在函数来实现圆函数。

这个函数看起来像这样:

float roundf(float x) {
    return signbit(x) ? ceil(x - 0.5) : floor(x + 0.5);
}

有没有办法使用 Neon 内在函数来做到这一点?如果没有,如何使用 Neon 内部函数来实现这个功能?

编辑

计算两个浮点数的乘积后,调用roundf(在armv7和armv8上)。

我的编译器是clang。

这可以通过 vrndaq_f32: https://developer.arm.com/architectures/instruction-sets/intrinsics/#f:@navigationhierarchiessimdisa=[Neon]&q=vrndaq_f32 for armv8 来完成。

如何在 armv7 上执行此操作?

编辑

我的实现

// input: float32x4_t arg
float32x4_t vector_zero = vdupq_n_f32(0.f);
float32x4_t neg_half = vdupq_n_f32(-0.5f);
float32x4_t pos_half = vdupq_n_f32(0.5f);

uint32x4_t mask = vcgeq_f32(arg, vector_zero);
uint32x4_t mask_neg = vandq_u32(mask, neg_half);
uint32x4_t mask_pos = vandq_u32(mask, pos_half);
arg = vaddq_f32(arg, (float32x4_t)mask_pos);
arg = vaddq_f32(arg, (float32x4_t)mask_neg);
int32x4_t arg_int32 = vcvtq_s32_f32(arg);
arg = vcvtq_f32_s32(arg_int32);

有没有更好的方法来实现这个?

【问题讨论】:

  • 许多霓虹灯指令都包含可选的舍入。根据您要使用它的位置,最好将舍入与之前的任何操作结合起来。
  • 你在做编译器吗?
  • 对于霓虹灯上的float 数据没有直接舍入指令。但是,您可以使用一个小数位转换为int,然后将其用于舍入。
  • 这是 32 位还是 64 位? AArch64 有 FRINTZ 指令,我认为这是你想要的。
  • 这是什么,四舍五入到最接近 0.0?所以它是 C roundf() 函数?正如 Nate 所说,这可以通过 AArch64 的 frint 来完成。 Clang 确实将 round() 内联为 frinta: godbolt.org/z/o8arE1795。但不适用于 ARM32。

标签: c arm rounding intrinsics neon


【解决方案1】:

定义你真正想要的哪种形式的舍入很重要。请参阅Wikipedia 了解有多少舍入选择。

从您的 code-sn-p 中,您要求 commercialsymmetric 舍入,从零开始舍入平局。对于 ARMv8 / ARM64,vrndaq_f32 应该这样做。

SSE4 _mm_round_ps 和 ARMv8 ARM-NEON vrndnq_f32 银行家四舍五入,即四舍五入到最近(偶数)。

【讨论】:

  • 你确定vrndaq_f32?问题中的手动链接说它四舍五入“到最近的关系Away”,即round()而不是nearbyint(),如_mm_round_ps。此外,如果 ARM 混合像 x86 vblendps 一样工作,您不需要 计算 选择掩码,您可以将 FP 位模式用作选择器。 -0.0 和 +0.0 仍会舍入某种零,我认为实际上保留了输入符号。 (如果您将相等性与 +0.0 进行比较,则不会发生这种情况)
  • 那么让我再次检查我在 ARM64 上的测试覆盖率。如果 vrndaq_f32 应该是像 C99 roundf 那么有些不对劲。
  • 对不起,我实际上使用的是vrndnq_f32,即FRINTN,它是银行家。 vrndaq_f32 是最接近的 FRINTA,距离零。
【解决方案2】:

您的解决方案在周期计数和寄存器利用率方面都非常昂贵。

提供-(2^30) <= arg < (2^30),你可以做到以下几点:

int32x4_t argi = vcvtq_n_s32_f32(arg, 1);
argi = vsraq_n_s32(argi, argi, 31);
argi = vrshrq_n_s32(argi, 1);
arg = vcvtq_f32_s32(argi);

除了arg 本身之外,它不需要任何其他寄存器,并且将使用 4 条廉价指令完成。它适用于aarch32aarch64

godblot link

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多