为什么程序可以在 java 或其他语言中准确显示无限重复的浮点数
why program can exactly display infinite repeating floating point number in java or other language
就像十进制数0.1,用二进制表示0.00011001100110011....,这是一个无限循环的数。
当我这样写代码时:
float f = 0.1f;
程序会将其四舍五入为二进制 0 01111011 1001 1001 1001 1001 1001 101,这不是原始数字 0.1。
但是当像这样打印这个变量时:
System.out.print(f);
我可以获得原始数字 0.1 而不是 0.100000001 或其他一些数字。我认为程序不能准确表示“0.1”,但可以准确显示“0.1”。怎么做?
我通过将二进制的每一位相加来恢复十进制数,看起来很奇怪。
float f = (float) (Math.pow(2, -4) + Math.pow(2, -5) + Math.pow(2, -8) + Math.pow(2, -9) + Math.pow(2, -12) + Math.pow(2, -13) + Math.pow(2, -16) + Math.pow(2, -17) + Math.pow(2, -20) + Math.pow(2, -21) + Math.pow(2, -24) + Math.pow(2, -25));
float f2 = (float) Math.pow(2, -27);
System.out.println(f);
System.out.println(f2);
System.out.println(f + f2);
输出:
0.099999994
7.4505806E-9
0.1
在数学中,f1 + f2 = 0.100000001145...,不等于 0.1。为什么程序不会得到像0.100000001这样的结果,我认为它更准确。
Java 的 System.out.print
打印出足够的小数,如果将结果表示解析为 double
或 float
,则可以将其转换为原始的 double
或 float
值。
这是个好主意,因为这意味着从某种意义上说,在这种十进制转换中不会丢失任何信息。另一方面,它会给人一种准确的印象,正如您在问题中明确指出的那样,这是错误的。
在其他语言中,您可以打印所考虑的 float
或 double
的精确十进制表示:
#include <stdio.h>
int main(){
printf("%.60f", 0.1);
}
结果:0.100000000000000005551115123125782702118158340454101562500000
在Java中,为了模拟上述行为,您需要将float
或double
转换为BigDecimal
(此转换是精确的)然后用足够的数字打印 BigDecimal
。 Java 对浮点到表示十进制的字符串转换的态度无处不在,因此甚至 System.out.format
也是 affected。链接的 Java 程序,其中重要的一行是 System.out.format("%.60f\n", 0.1);
,显示 0.100000000000000000000000000000000000000000000000000000000000
,虽然 0.1d
的值是 而不是 0.100000000000000000000 ...,并且 Java 程序员可能会因为期望与 C 程序相同的输出而被原谅。
要将 double
转换为表示 double
精确值的字符串,请考虑将 hexadecimal format, that Java supports 用于文字和打印。
我相信Double.toString(double)
(and similarly in Float#toString(float)
):
How many digits must be printed for the fractional part of m or a? There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type double. That is, suppose that x is the exact mathematical value represented by the decimal representation produced by this method for a finite nonzero argument d. Then d must be the double value nearest to x; or if two double values are equally close to x, then d must be one of them and the least significant bit of the significand of d must be 0.
(我的重点)
就像十进制数0.1,用二进制表示0.00011001100110011....,这是一个无限循环的数。 当我这样写代码时:
float f = 0.1f;
程序会将其四舍五入为二进制 0 01111011 1001 1001 1001 1001 1001 101,这不是原始数字 0.1。 但是当像这样打印这个变量时:
System.out.print(f);
我可以获得原始数字 0.1 而不是 0.100000001 或其他一些数字。我认为程序不能准确表示“0.1”,但可以准确显示“0.1”。怎么做?
我通过将二进制的每一位相加来恢复十进制数,看起来很奇怪。
float f = (float) (Math.pow(2, -4) + Math.pow(2, -5) + Math.pow(2, -8) + Math.pow(2, -9) + Math.pow(2, -12) + Math.pow(2, -13) + Math.pow(2, -16) + Math.pow(2, -17) + Math.pow(2, -20) + Math.pow(2, -21) + Math.pow(2, -24) + Math.pow(2, -25));
float f2 = (float) Math.pow(2, -27);
System.out.println(f);
System.out.println(f2);
System.out.println(f + f2);
输出:
0.099999994
7.4505806E-9
0.1
在数学中,f1 + f2 = 0.100000001145...,不等于 0.1。为什么程序不会得到像0.100000001这样的结果,我认为它更准确。
Java 的 System.out.print
打印出足够的小数,如果将结果表示解析为 double
或 float
,则可以将其转换为原始的 double
或 float
值。
这是个好主意,因为这意味着从某种意义上说,在这种十进制转换中不会丢失任何信息。另一方面,它会给人一种准确的印象,正如您在问题中明确指出的那样,这是错误的。
在其他语言中,您可以打印所考虑的 float
或 double
的精确十进制表示:
#include <stdio.h>
int main(){
printf("%.60f", 0.1);
}
结果:0.100000000000000005551115123125782702118158340454101562500000
在Java中,为了模拟上述行为,您需要将float
或double
转换为BigDecimal
(此转换是精确的)然后用足够的数字打印 BigDecimal
。 Java 对浮点到表示十进制的字符串转换的态度无处不在,因此甚至 System.out.format
也是 affected。链接的 Java 程序,其中重要的一行是 System.out.format("%.60f\n", 0.1);
,显示 0.100000000000000000000000000000000000000000000000000000000000
,虽然 0.1d
的值是 而不是 0.100000000000000000000 ...,并且 Java 程序员可能会因为期望与 C 程序相同的输出而被原谅。
要将 double
转换为表示 double
精确值的字符串,请考虑将 hexadecimal format, that Java supports 用于文字和打印。
我相信Double.toString(double)
(and similarly in Float#toString(float)
):
How many digits must be printed for the fractional part of m or a? There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type double. That is, suppose that x is the exact mathematical value represented by the decimal representation produced by this method for a finite nonzero argument d. Then d must be the double value nearest to x; or if two double values are equally close to x, then d must be one of them and the least significant bit of the significand of d must be 0.
(我的重点)