【发布时间】:2013-03-21 13:37:01
【问题描述】:
我有关于双精度的问题。当浮点值传递给双精度时,我会得到一些不同的结果。例如
float f= 54.23f;
double d1 = f;
System.out.println(d1);
输出为 54.22999954223633。有人可以解释这种行为背后的原因。是否像双精度默认为 14 位小数精度。
【问题讨论】:
标签: java floating-point double
我有关于双精度的问题。当浮点值传递给双精度时,我会得到一些不同的结果。例如
float f= 54.23f;
double d1 = f;
System.out.println(d1);
输出为 54.22999954223633。有人可以解释这种行为背后的原因。是否像双精度默认为 14 位小数精度。
【问题讨论】:
标签: java floating-point double
float 和 double 打印相同的值不同,因为 Java 规范要求打印尽可能多的数字,以区分相同类型中相邻的可表示值(根据我的回答 here,请参阅linked documentation 以获得更精确的定义)。
由于float 用于表示值的位更少,因此值也更少,因此它们之间的间距更大,并且您不需要那么多数字来区分它们。当您将值放入 double 并打印它时,Java 规则要求打印更多数字,以便将该值与附近的 double 值区分开来。 println 函数不知道该值最初来自 float 并且不包含可以放入 double 中的尽可能多的信息。
54.23f 正好是 54.229999542236328125(十六进制,0x1.b1d70ap+5)。正下方和正上方的 float 值是 54.2299957275390625 (0x1.b1d708p+5) 和 54.23000335693359375 (0x1.b1d70cp+5)。如您所见,打印“54.229999”会将值与 54.229995... 和 54.23... 区分开来。但是,double 的值刚好低于和高于 54.23f 是 54.22999954223632101957264239899814128875732421875 和 54.229999542236335230427357601001858711224。要区分值,需要“54.22999954223633”。
【讨论】:
float 到 double 转换的一个原因是,即使从位级别的角度来看,像 0x1.B1D708A+5 这样的东西是幂的完美表示-二分数,浮点变量更典型地使用作为不是二分数幂的数字的不完美表示。如果我知道最接近某个数字的float 是 0x1.B1D708A+5,这将意味着该数字在某个范围内,但应该被认为与该范围内的其他值无法区分。将其转换为 double...
float 可以表示的数字范围的中心,即使在实践中可能没有该假设的合理基础。将 16777217f 和 16777216.0001 视为排名相同,恕我直言,与将前一个值视为排名较低(如果比较运算符将两个操作数隐式转换为 double 会发生这种情况)相比,恕我直言。
float 和 double 都不能携带任何关于它周围区间的信息。它代表一个数字。有些人认为它代表了一个区间,例如,相邻值的中点。但是,只有在执行了单个操作(例如从十进制到二进制浮点的转换)时,这才是正确的。经过多次操作后,误差可能会更大,并且没有限制它如何增长。因此,如果要表示区间信息,就必须在单独的数据中携带……
float 到double 的转换是无关紧要的,因为它既不会改变转换后的值,也不会改变单独的数据。通过符合规范而不是通过以已知不正确的方式使用浮点来实现正确计算。浮点值的 Java 默认显示应仅用作值的指示,而不是精度或间隔的指示。
double 计算没有足够的步骤或足够的灾难性取消将错误推到 2^29ulps,这将是从float 投射的double 的起点 点。此外,像浮点文字这样的东西应该符合“精确操作数的单个操作的结果”。
这是因为 float 隐藏了多余的小数,而 double 显示了它们。 double 将非常精确地表示实际数字并显示更多数字。
试试这个:
System.out.println(f.doubleValue()); (当然首先需要使其成为 Float)
如您所见,信息就在那里,只是四舍五入。 希望这会有所帮助
【讨论】:
f.doubleValue()很可能实现为new Double(f)。
return (double)value;
double 并不总是显示更精确;有时它显示的结果与在float 中打印相同的值相同。实际的规则是打印的数字必须是这样的,即传递给打印的值是最接近数字值的类型的值(有一些关于关系的附加规则)。这通常会导致 double 显示的位数多于 float,但并非总是如此。
这是由于Internal Representation.
浮点数通常作为符号位、指数字段和有效数(尾数)从左到右打包到计算机数据中。
这被称为Accuracy Problems。
浮点数不能精确地表示所有实数,并且浮点运算不能精确地表示真正的算术运算,这一事实导致了许多令人惊讶的情况。这与计算机通常表示数字的有限精度有关。
这不是问题。这就是双重的工作方式。您不必处理它并关心它。 double 的精度就足够了。想一想,你的数字和预期结果之间的差异在小数点后的第 14 位。
如果您需要任意高的精度,请使用 java.math.BigDecimal 类。
或者,如果您仍想使用双精度。这样做:
double d = 5.5451521841;
NumberFormat nf = new DecimalFormat("##.###");
System.out.println(nf.format(d));
如有任何疑问,请告诉我。
【讨论】:
54.23f 没有值 54.23 而是关于为什么将其打印为 double 显示的数字比将其打印为 float 显示更多的数字,即使打印相同的值。
实际上这只是关于不同的视觉表示或将float / double 转换为String。我们来看看内部二进制表示
float f = 0.23f;
double d = f;
System.out.println(Integer.toBinaryString(Float.floatToIntBits(f)));
System.out.println(Long.toBinaryString(Double.doubleToLongBits(d)));
输出
111110011010111000010100011111
11111111001101011100001010001111100000000000000000000000000000
表示f转换为d1没有任何失真,有效数字相同
【讨论】:
double 和float 表示不同格式的数字。
因此,您一定会发现某些数字可以完美地以一种格式存储,但不能以另一种格式存储。您碰巧找到了一个正确地适合 float 但不适合 exactly 的 `double。
当使用两种不同的格式化程序时,也会出现此问题。
【讨论】:
double 使用与float 相同的基本格式,只是使用了更多位。每个float 值都应该完全可以表示为double。
float,都存在一个具有相同标称值的double,但浮点数的含义超出了标称值。考虑以下问题:“一个 3.0 厘米的钉子适合 3.002 厘米的孔吗?”和“3.000 厘米的钉子能装进 3.002 厘米的洞吗?”两个钉子的标称尺寸完全相同,但后者的钉子被指定为适合而前者不是(即使它是 3.004 厘米,它仍然可以被描述为 3.0 厘米的钉子)。没有与任何float 值具有相同“含义”的double 值;特别是对于两个值,没有double 甚至接近。