Java float 比 double 更精确?

问题描述 投票:0回答:4

代码:

class Main {
    public static void main (String[] args) {
        System.out.print("float: ");
        System.out.println(1.35f-0.00026f);
        System.out.print("double: ");
        System.out.println(1.35-0.00026);
    }
}

输出:

float: 1.34974
double: 1.3497400000000002

??? float 得到了正确的答案,但是 double 却不知从何而来添加了额外的东西,为什么??

double 不是应该比 float 更精确吗?

java floating-accuracy floating-point-precision double-precision
4个回答
12
投票

float 的宽度为 4 个字节,而 double 的宽度为 8 个字节。

检查每个计算机科学家应该了解的浮点运算知识

当然,双精度数具有更高的精度,因此舍入误差稍小。

将无限多个实数压缩为有限个位数 需要一个近似表示。虽然有无限个 许多整数,在大多数程序中,整数计算的结果可以 以32位存储。相反,给定任何固定数量的位数, 大多数实数计算都会产生以下数量 无法使用那么多位来精确表示。 因此 浮点计算的结果通常必须按顺序舍入 适应其有限表示。 这个舍入误差是 浮点计算的特征。

旁注:-

我建议如果您想要精确的十进制值,请使用

java.math.BigDecimal


3
投票

造成这种情况的原因是浮点计算的数字机制。 此外,当 java 尝试打印浮点值时,它会截断尾随零。 而且,您知道,double 类型使用 8 个字节,而 float 使用 4 个字节。因此 double 精度更大。

因此,当 java 计算浮点值时,它会得到类似 1.3497400 的值,并在输出之前将其截断。 当java计算你的双精度值时,它会得到更多的数字,所以你会得到这样的结果。

尝试执行这个简单的示例以更好地理解:

public class Test {
    public static void main( String[] args ) {
        float sum = 0;
        for (int i = 1; i <= 10; i++) {
            sum += 0.1;
        }
        System.out.println( sum );
    }
}

public class Test {
    public static void main( String[] args ) {
        double sum = 0;
        for (int i = 1; i <= 10; i++) {
            sum += 0.1;
        }
        System.out.println( sum );
    }
}

2
投票

如果您了解转换 double 值的规则 to strings,由 Double.toString [Java-API] 的文档指定,您知道程序打印足以区分 double 值的最短小数部分 从最近的邻居开始,前后至少有一位数字 小数点。

查看 Joshua Bloch 的 Java 谜题:陷阱、陷阱和角落案例 - 谜题 2:是时候做出改变了。他在那一章中解释了这个问题。


0
投票

相反,你看到你所看到的因为

double
float
更精确,而不是尽管如此。您所看到的是每个类的默认
toString
操作如何将浮点数转换为字符串的结果,这部分取决于浮点数格式的精度。

TL;DR:

float
double
的默认“字符串”操作都会创建仅包含所需数量的字符串,以唯一标识
float
double
中相对于采用相同格式的相邻可表示值,而不是输出该值的所有数字。由于
double
可以比
float
表示更多的值,因此它的字符串必须更精确。

详情:

让我们从这里开始:从

1.35f - 0.00026f
获得的实际值不是
1.34974
,而是
1.34974002838134765625
(因为,正如您可能知道的那样,并非所有浮点数都可以用
float
double
格式
)。
double
等效的
1.35 - 0.00026
为您提供更准确的值:
1.349740000000000161861635206150822341442108154296875
。 (我使用了 baseconvert.com 的 IEEE-754 转换器,根据 h-schmidt.net 仔细检查了 32 位结果,并使用下面的代码。)让我们通过使用
BigDecimal
来看看将这些值格式化为字符串而不是
toString
live copy):

import java.math.BigDecimal;

public class Example {
    private static String format(float value) {
        return new BigDecimal(value).toPlainString();
    }
    private static String format(double value) {
        return new BigDecimal(value).toPlainString();
    }
    public static void main(String[] args) throws Exception {
        float  f = 1.35f - 0.00026f;
        double d = 1.35  - 0.00026;

        System.out.println("Float:  " + format(f));
        // "Float:  1.34974002838134765625"

        System.out.println("Double: " + format(d));
        // "Double: 1.349740000000000161861635206150822341442108154296875"
    }
}

事实上,

1.35
0.00026
都不能被
float
double
精确保持,因此甚至在减法之前就开始出现不精确性。

好吧,那么为什么

float
toString
只给你
"1.34974"
,而
double
toString
却给你
"1.3497400000000002"
?因为两个类的
toString
方法(
float
double
)都会生成一个值的最短(通常)输出,该值将其与两侧相邻的可表示值区分开来。

使用

float
,因为
float
相当不精确,短字符串
"1.34974"
足以识别
float
实际包含的值(而不是它旁边的值之一)。也就是说,即使实际值是
1.34974002838134765625
,也不需要所有这些数字来将其与其他可表示的
float
值区分开来,在
float
领域中您所需要的只是
"1.34974"
Float.parseFloat("1.34974")
再次为您提供
float
1.34974002838134765625

但对于

double
则不然,因为
double
float
具有更多可表示的值,因此字符串
"1.34974"
不能唯一标识
double
1.349740000000000161861635206150822341442108154296875
。事实上,如果将
"1.34974"
解析为
double
,则会得到
1.3497399999999999398170302811195142567157745361328125
。因此,它必须包含更多数字来区分您正在制作字符串的
double
值与其他
double
值 - 因为
double
float
更精确。

© www.soinside.com 2019 - 2024. All rights reserved.