【问题标题】:Vectorized floating point rounding using NEON使用 NEON 的矢量化浮点舍入
【发布时间】:2012-05-26 00:06:02
【问题描述】:

我有一个 NEON 寄存器,里面填满了float32。我想将它们四舍五入到最接近的整数,而不必传输回主 CPU。将float32 转换为uint32 的NEON 指令只是截断,例如39.7 变为 39,而不是 40。我不太关心0.5 的处理方式——从零舍入或舍入到甚至两者都对我有用。

我认为实现舍入的最佳途径是

  • 转换为int32(因此被截断)
  • 转换返回float32
  • int32 加1,转换回float32,并留出以防我们四舍五入
  • 减法
  • 0.5 比较(不需要绝对值,因为我知道在我的情况下它们都是正数)
  • 根据比较结果选择截断或截断 + 1

这看起来丑陋、缓慢且复杂。

有没有更清洁、更快、更简单、更明智的方法?

【问题讨论】:

    标签: floating-point arm rounding neon


    【解决方案1】:

    添加 .5 并转换为整数。如果您想要浮点格式的结果,请转换回来。

    既然你知道数字都是正数,另一个选择是加 0x1p23 并减去 0x1p23。加上 0x1p23 的结果至少是 0x1p23,所以浮点结果没有小于 1 的位,所以它肯定已经四舍五入为整数。然后减去0x1p23减去添加的值,只留下四舍五入的效果。

    更新:如果输入在 [0x1p47, 0x1p48) 并且其低位为 1,则第二种方法将失败。那么 0x1p23 是输入的 ULP 的一半,所以加法会导致向上舍入(到偶数),而减法没有效果。我认为有一个修改可以解决这个问题,但我手头没有。

    【讨论】:

    • 哈哈哈。我是一个白痴。 :) 感谢 0x1p23 提示,这很有趣。
    • 其实0x1p23如果输入在[0x1p47, 0x1p48)中且低位为1,则结果不正确。那么 0x1p23 是输入的 ULP 的一半,所以加法会导致向上舍入(到偶数),而减法没有效果。我认为有一个修改可以解决这个问题,但我手头没有。
    • 我觉得要加减的值是0x1.8p+23
    • @kanna:0x1.8p+23 不适用于 x = 0x1p22 + 1 (4,194,305)。那么 x + 0x1.8p+23 将是 0x2p24 + 1 (16,777,217) 与实数算术,但这是不可表示的,所以产生 0x2p24 (16,777,216)。然后减去 0x1.8p23 得到 0x1p22 (4,194,304)。
    【解决方案2】:

    我们知道,浮点数到整数轮次需要根据正数或负数加减 0.5。在 Neon 中, 1. 我可以提取有符号的值; 2. 与0.5位或,则0.5有符号; 3. 添加带符号的 0.5 和原始值:

    // 1. extract sign of origin value
    int32x4_t reinterpretInt = vreinterpretq_s32_f32(inputFloat);
    int32x4_t signExtract = vdupq_n_s32(-2147483648);
    int32x4_t signSignal = vandq_s32(reinterpretInt, signExtract);
    
    // 2. bit-or with 0.5 with origin value
    float32x4_t roundValue = vdupq_n_f32(0.5);
    float32x4_t plusValue = vreinterpretq_f32_s32(vorrq_s32(vreinterpretq_s32_f32(roundValue), signSignal));
    
    // 3. add signed 
    return vaddq_f32(inputFloat, plusValue);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多