为何浮点数精确相等比较在黄金比例计算中始终有效?
为什么这个用浮点数精确相等比较的函数总能返回黄金比例?
我们常被教导不要对浮点数进行精确相等比较,但下面这个接收任意正数、返回黄金比例的函数,却用了double类型的精确相等判断,而且始终有效:
public static double f(double x) { double y; while ((y = 1 + 1 / x) != x) x = (x + y) / 2; return x; } @Test void test() { assertEquals((1 + sqrt(5)) / 2, f(1.0)); // 测试通过! }
原本以为只有部分输入能生效,但用JQwik做属性测试后,所有正输入都能通过:
@Property void test2(@ForAll @Positive double x) { assertEquals((1 + sqrt(5)) / 2, f(x)); // 全部通过! }
为什么这个循环里从未出现浮点数差值极小、无法触发精确相等的情况?
核心原因解析
黄金比例的浮点数特性
真实的黄金比例φ满足数学等式:φ = 1 + 1/φ(等价于φ² = φ + 1)。而double是64位固定精度的浮点数,它对φ的近似值记为φ₀。当迭代收敛到φ₀时,在double的运算规则下,计算1 + 1/φ₀的结果会恰好等于φ₀——浮点数运算的舍入规则会让这个计算结果对齐到φ₀,而非产生一个接近但不等的值。迭代过程的单调收敛性
无论初始输入的正x是大于还是小于φ:
- 如果
x > φ,那么y = 1 + 1/x必然小于φ,两者的平均值(x+y)/2会比原x更靠近φ; - 如果
x < φ,那么y = 1 + 1/x必然大于φ,平均值同样会向φ靠拢。
这种单调收敛的特性,会让迭代快速逼近φ₀,不会出现来回震荡或无限逼近却达不到相等的情况。
- 有限步数内必然收敛
double的精度约为15-17位有效数字,这个迭代过程只需要几步就能收敛到满足y == x的φ₀,不会陷入无限循环,也不会停留在差值极小但不等的状态。
内容的提问来源于stack exchange,提问作者k314159
相关产品推荐
相关产品推荐

