定点数学 - 从浮点安全到总是产生相同结果的转换?

Fixed-Point-Math - Is the conversion from float safe to always yield the same result?

我正在使用 libfixmath 进行模拟,需要同时在两个设备 (iOS / Android) 上进行并且非常准确。

模拟需要输入一些初始浮动参数。我想知道,使用浮点数然后按照下面的方式将它们转换为 fix16_t 是否安全(该函数来自库),或者我是否需要已经为模拟提供 fix16_t 值?

因此,是否有可能由于浮点不准确,两个不同的设备使用相同的输入为下面的函数计算出不同的结果?

typedef int32_t fix16_t;
static const fix16_t fix16_one = 0x00010000; /*!< fix16_t value of 1 */

static inline fix16_t fix16_from_float(float a)
{
    float temp = a * fix16_one; 

    // rounding
    temp += (temp >= 0) ? 0.5f : -0.5f;
    return (fix16_t)temp;
}

假设:

  • 两台机器都使用 IEEE-754 单精度浮点表示 float

  • a 是 "reasonable"

转换应该是可移植的,除了 a 的绝对值略小于 0.5×2−16 的情况。

将(二进制)浮点数乘以 2 的幂(在本例中为 216)是精确的,前提是它不会导致浮点溢出(或在 2) 的负幂的情况下下溢。每个浮点实现都应该以完全相同的方式处理乘法。

C++ 标准要求从浮点数到整数类型的转换才能截断到 0,因此舍入策略是正确的。

将 0.5 添加到 temp 几乎在所有情况下都会产生正确的结果。

对于 temp 的中间值,结果将是精确的。

如果temp大于223,相加没有效果,但是没有小数要四舍五入,所以最后的结果是可想而知的只要在转换回整数时没有溢出。

如果temp小于1.0,和会不精确,因为指数会增加。然后加法应该 round 以产生正确的结果。在这里,唯一感兴趣的情况是截断和可能为 0 或 1;如果 temp 不接近 0.5,则和不能为 1.0,截断和必须为 0。如果 temp 至少为 0.5,则和必须至少为 1.0,截断和必须为1.

但如果 temp 略小于 0.5,则总和的四舍五入可能很重要。特别是,如果 temp 恰好是 0.5−2−25,则存在歧义。总和的结果将为 1.0−2−25,但该值不能精确表示为 IEEE-754 single-precision 浮点数。而且,错误项恰好是ULP的one-half。所以结果需要四舍五入,那会服从实现的四舍五入模式。

IEEE-754 的默认舍入模式是 "banker's rounding", where rounding of a value of exactly one-half is towards whichever of the two possibilities has a 0 as its low-order bit. That will favour rounding 0.5−2−25 + 0.5 to 1.0, which will produce the incorrect integer truncation 1. However, it is possible that a given implementation uses a different rounding mode, perhaps because it has been set using std::fesetround

以上所有内容同样适用于负值。