代码:
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 更精确吗?
float 的宽度为 4 个字节,而 double 的宽度为 8 个字节。
当然,双精度数具有更高的精度,因此舍入误差稍小。
将无限多个实数压缩为有限个位数 需要一个近似表示。虽然有无限个 许多整数,在大多数程序中,整数计算的结果可以 以32位存储。相反,给定任何固定数量的位数, 大多数实数计算都会产生以下数量 无法使用那么多位来精确表示。 因此 浮点计算的结果通常必须按顺序舍入 适应其有限表示。 这个舍入误差是 浮点计算的特征。
旁注:-
我建议如果您想要精确的十进制值,请使用
java.math.BigDecimal
造成这种情况的原因是浮点计算的数字机制。 此外,当 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 );
}
}
如果您了解转换 double 值的规则 to strings,由 Double.toString [Java-API] 的文档指定,您知道程序打印足以区分 double 值的最短小数部分 从最近的邻居开始,前后至少有一位数字 小数点。
查看 Joshua Bloch 的 Java 谜题:陷阱、陷阱和角落案例 - 谜题 2:是时候做出改变了。他在那一章中解释了这个问题。
相反,你看到你所看到的因为
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
更精确。