哪些操作可以使浮点数离开 [0, 1] 范围?

What operations can make floats leave a [0, 1] range?

我经常使用范围为 [0, 1] 的 floatdouble 类型。我知道浮点运算是不精确的,所以我通常会限制我的值,以保证它们在这个范围内 before/after 操作。

在某些情况下,我依赖于浮点数,甚至不是轻微的负值,而是恰好 <= 1,因此这是必要的。

例如,在这些功能中是否有必要:

// x and y are guaranteed to be in [0, 1]
float avg(float x, float y) {
    // the average of [0, 1] values should always be in [0, 1]
    return std::clamp<float>((x + y) / 2, 0, 1);
}

float mul(float x, float y) {
    // the product of [0, 1] values should always be in [0, 1]
    return std::clamp<float>(x * y, 0, 1);
}

float pow(float x, unsigned y) {
    // raising an [0, 1] value to any unsigned power should also result in an [0, 1] value
    return std::clamp<float>(std::pow(x, y), 0, 1);
}

算术运算什么时候可以让浮点数离开[0, 1]范围,是否有一致的规则?

如果可以,请将此答案限制为 IEEE754。

012 都可以精确表示为 float。算术运算符需要 return 可能的最佳浮点值。由于 xy 都不大于 1,因此它们的总和不能大于 2,否则总和会存在更好的 float。换句话说,小于1的两个float之和不能大于2.

同样适用于产品。

第三个需要夹具,因为不能保证 std::pow(x, y) return 是最好的 float

角题

float pow(float x, unsigned y) {
    // raising an [0, 1] value to any unsigned power should also result in an [0, 1] value
    return std::clamp<float>(std::pow(x, y), 0, 1);
}

使用 std::pow(±0, 0) 可能会导致超出 [0..1] 范围(或导致域错误),因为该结果未由 std::pow() 指定,也未经过数学解析:Zero to the power of zero .

如果遵循 IEEE,结果为 1,但不需要符合要求的库。

如果不遵循 IEEE 和 std::pow(0,0) return 的 NAN,我预计 std::clamp(NAN,0,1) 也会 return NAN 并破坏系统。

候选替代代码

return (y==0) ? 1 : x;

请注意函数的结果可能是 -0.0,但该值仍在 [0...1] 范围内。

我认为 mul()avg() 没有关于 -0.0 的问题,以更高的精度和变体舍入模式进行中间计算。