我遇到了愚蠢的问题,从未见过它。
我正在尝试为一些需要增加10倍的数学计算做一个非常简单的任务。
首先,我写了一个非常简单的增长循环,但是我不理解,获得的值越高,得到的结果越不精确。
我想知道为什么会这样?以及如何解决它?
我的测试代码:
public class PowerOfTen {
private int lineCounter = 1;
public static void main(String[] args) {
PowerOfTen powerOfTen = new PowerOfTen();
powerOfTen.growingOfTenMethodOne();
powerOfTen.growingOfTenMethodTwo();
}
public void growingOfTenMethodOne() {
double MAX_VALUE = 1e50;
lineCounter = 1;
for (double i = 1; i < MAX_VALUE; i = i * 10) {
System.out.printf("%03d%1s%f\n", lineCounter, " | ", i);
lineCounter++;
}
}
public void growingOfTenMethodTwo() {
double MAX_VALUE = 50;
lineCounter = 1;
for (double i = 0; i < MAX_VALUE; i++) {
System.out.printf("%03d%1s%f\n", lineCounter, " | ", Math.pow(10, i));
lineCounter++;
}
}
}
两种方法都有效,并且假设返回正确的结果,但是在这两种方法中都会给出一些不准确的结果,如下面的示例所示。
方法1: 第24,26,27,28,31,32,33行未返回正确的结果
022 | 1000000000000000000000.000000
023 | 10000000000000000000000.000000
024 | 99999999999999990000000.000000
025 | 1000000000000000000000000.000000
026 | 9999999999999999000000000.000000
027 | 99999999999999990000000000.000000
028 | 999999999999999900000000000.000000
029 | 10000000000000000000000000000.000000
030 | 100000000000000000000000000000.000000
031 | 999999999999999900000000000000.000000
032 | 9999999999999999000000000000000.000000
033 | 99999999999999990000000000000000.000000
方法2: 第24,30行没有返回正确的结果
022 | 1000000000000000000000.000000
023 | 10000000000000000000000.000000
024 | 99999999999999990000000.000000
025 | 1000000000000000000000000.000000
026 | 10000000000000000000000000.000000
027 | 100000000000000000000000000.000000
028 | 1000000000000000000000000000.000000
029 | 10000000000000000000000000000.000000
030 | 100000000000000010000000000000.000000
031 | 1000000000000000000000000000000.000000
032 | 10000000000000000000000000000000.000000
033 | 100000000000000000000000000000000.000000
答案 0 :(得分:6)
这不是一个&#34;愚蠢的问题&#34 ;;它是计算机科学的核心问题之一......处理数字的正确表示是一项非平凡的任务。
例如,您可能希望开始阅读here。
任何编程&#34;编程&#34;应该明白这实际意味着什么;以及它如何影响您的应用程序/解决方案。
答案 1 :(得分:4)
这是因为从双精度浮点格式转换为十进制格式。就像有十进制的周期性数字一样,有些数字是二进制的周期性的,但不是十进制的。
这些案例是二进制的。
答案 2 :(得分:3)
Java浮点double
始终是IEEE754 64位浮点类型。
方法1。
浮点数双可以精确表示整数,最高可达到第42次幂。因此,一旦超过9,007,199,254,740,992,就会出现不准确的情况。 (这是一个常见的误解,浮点总是会引入不准确性:在您的特定情况下,计算对于循环的前几次迭代将是精确的。)
方法2。
如果两个参数是浮点,则 Math.pow(x, y)
实现为exp(y log x)。这将引入数值精度问题,因为浮点双精度仅精确到约15个有效数字。
答案 3 :(得分:0)
这一切都归结为Java规范。
在Java中,整数使用32位来表示其值。 FLOAT 使用24位尾数,因此大于2 ^ 23的整数将截断其最低有效位。例如33554435(或0x200003)将被截断为大约33554432 +/- 4. DOUBLE 使用53位尾数,因此将能够表示32位整数而不会丢失数据。
检查this article,以非常简单的方式解释所有内容。