x 的平方的平方根等于 x

square root of square of x equals x

给定一个双精度浮点(非负)数x,它的平方根是否总是等于它本身?

换句话说,如果执行以下操作是否会导致精度损失:

x = <non-negative double>
y = x^2
z = sqrt(y)

这样:

x == z

我对平方变为无穷大或零的情况不感兴趣,只对符合双精度的数字感兴趣。

对一个数求平方,生成的值是原始值位数的两倍。因此,如果 x 太大,则 x^2 中的某些位会丢失,并且 x 无法从 y 中完全恢复 [编辑:仍然可以通过适当的舍入从 y 中获取 x]。在 IEEE-754 双精度的情况下,如果 x 的有效数部分超过 26 位,则 y 的结果将被截断。这是最简单的情况。

如果 x 的有效数位很少,但指数很大或很小,那么 x^2 对于双精度来说可能太大,将变成 inf 或非正规数,在这种情况下无法恢复 x.

如果 x 不是太大或太小那么 sqrt(y) 将等于 x 因为 IEEE-754 标准要求 +, - , *, /sqrt 适当舍入.

Examples:

#include <iostream>
#include <ios>
#include <iomanip>
#include <cmath>

using std::fixed;
using std::hexfloat;
using std::cout;

int main() {
    double x = 1.25e155;
    double y = x*x;
    cout << hexfloat << "x = " << x << ", y = " << y << ", sqrt(y) = " << sqrt(y) << '\n';
    
    x = 1.25e-155;
    y = x*x;
    cout << hexfloat << "x = " << x << ", y = " << y << ", sqrt(y) = " << sqrt(y) << '\n';
    return 0;
}
#include <stdio.h>
#include <math.h>

int main(void) {
  double x = 1.0000000000000001E-160;
  double square = x*x;
  double root = sqrt(square);
  if (root != x) {
    printf("%.20g\n", x);
    printf("%.20g\n", root);
  }
}

产出

1.0000000000000001466e-160
9.9999443357584897793e-161

这里发生的事情是 x 足够大以至于它的平方不为零,但又足够小以至于它的平方只能表示为非规范化数字,这会降低可用精度。

我的印象是@MarkDickinson 对@LưuVĩnhPhúc 的回答的评论在很大程度上是正确的。如果 xx*x 都是正归一化数字,那么即使使用快速蛮力(在几个小范围内),我也无法找到 x != sqrt(x*x) 的示例,尽管这应该不被视为证据。