我们可以假设所有正整数 x == (int)sqrt(x * x) 吗?

Jeg*_*ovs 5 c c++ java math

在 C++ 中,该sqrt函数仅对double值进行操作。

如果我们使用整数(unsigned long long),我们可以确定

x == sqrt(x * x)
Run Code Online (Sandbox Code Playgroud)

对于任何积极的x地方x * x <= MAXIMUM_VALUE

它取决于机器架构和编译器吗?

rzw*_*oot 7

在 Java 中,Math.sqrt(x)采用双精度值。你说 xx * x在下面Integer.MAX_VALUE。每个整数都可以用 double 完美表示 -double在 java 中被明确定义为带有 52 位尾数的 iEEE-754 风格的 double;因此在 java 中 adouble可以完美地表示 -2^52 和 +2^52 之间的所有整数值,这很容易涵盖所有int值(因为在 java 上定义为有符号 32 位),但它不涵盖所有long。(定义为有符号的 64 位;64 大于 52,所以不行)。

因此,x * x不损失精度时它结束了从得到转换intdouble。然后,Math.sqrt()在这个数字上会给出一个结果,它也可以完美地表示为 a double(因为它是x,并且鉴于它x*x适合 an intx也必须适合),因此,是的,这将始终适用于所有x

但是,嘿,为什么不试一试,对吧?

public static void main(String[] args) {
    int i = 1;
    while (true) {
        if (i * i < 0) break;
        int j = (int) Math.sqrt(i * i);
        if (i != j) System.out.println("Oh dear! " + i + " -> " + j);

        i++;
    }
    System.out.println("Done at " + i);
}

> Done at 46341
Run Code Online (Sandbox Code Playgroud)

因此,通过彻底尝试这一切来证明这一点。

事实证明,没有这样的-任何long值,这样x * x仍然适用(因此,<2^63-1)具有如下性质x == (long) Math.sqrt(x * x);。这大概是因为 at x*x,该数字完全适合 long,即使并非所有这么大的整数都如此。证明:

long p = 2000000000L;
for (; true; p++) {
    long pp = p * p;
    if (pp < 0) break;
    long q = (long) Math.sqrt(pp);
    if (q != p) System.out.println("PROBLEM: " + p + " -> " + q);
}
System.out.println("Abort: " + p);

> Abort: 3037000500
Run Code Online (Sandbox Code Playgroud)

当然,如果存在任何不成立的数字,那么在这个高端范围内至少有一个。从 0 开始需要很长时间。

但是我们是否知道 sqrt 总是会返回一个完全平方的精确值,或者它可能有点不准确?

我们应该 - 它是 java。与 C 不同,几乎所有东西都是“明确定义的”,如果 JVM 无法产生指定的确切答案,则它不能合法地称自己为 JVM。Math.sqrt文档提供的余地不足以让任何答案准确x地成为合法实施,因此,是的,这是一个保证

理论上,JVM 对浮点数有一些非常小的余地,这strictfp会禁用,但 [A] 更多的是使用 80 位寄存器来表示数字而不是 64,这不可能破坏这个假设,[B] 不久前出现了一个java标记问题,表明strictfp对任何硬件和任何 VM 版本都有任何影响,唯一可行的结果是 15 年前不可重现的事情。我非常有信心地说这将始终成立,无论硬件或 VM 版本如何。