如果分数不能精确地用二进制表示,Double.toString()如何工作?

hli*_*hli 4 java floating-point bigdecimal

我无法理解Double.toString()在Java / JVM中的工作方式。我的理解是,一般而言,分数不能精确地用Double和Float等浮点类型表示。例如,206.64的二进制表示形式是206.6399999999999863575794734060764312744140625。那么,为什么(206.64).toString()返回“ 206.64”而不是“ 206.6399999999999863575794734060764312744140625”?

Kotlin中的测试代码。

@Test
fun testBigDecimalToString() {
    val value = 206.64
    val expected = "206.64"

    val bigDecimal = BigDecimal(value)

    assertEquals(expected, value.toString()) // success
    assertEquals(expected, bigDecimal.toString()) // failed. Actual: 206.6399999999999863575794734060764312744140625
}
Run Code Online (Sandbox Code Playgroud)

Eri*_*hil 7

打印a float或a 时看到的位数double是Java的默认规则(从float和转换double为十进制)的结果。

Java的浮点数默认格式使用最少的有效十进制数字来将数字与附近的可表示数字区分开。1个

在您的示例中,206.64在源文本double中将其转换为值206.6399999999999899875775794734060764312744140625,因为在该double类型可表示的所有值中,最接近206.64的值。下一个较低的值和下一个较高的值是206.639999999999957935870043002068996429443359375和206.640000000000014779288903810083866119384765625。

在打印此值时,Java只需要打印“ 206.64”,因为这足以使我们可以double从其邻居206.639999999999957957935870043002068996429429359359375和206.6400000000000147792889030100838661193847656中选择值206.6399999999999863575794734060764312744140625。请注意,从206.63999…的9s末尾开始,第一个值与206.64的差值为1.1364…,而第三个值206.64000…的差值则为.1477…。因此,当Java打印“ 206.64”时,表示double正在打印的值是最接近的可表示值,即206.6399999999999863575794734060764312744140625值,而不是更远的206.6400000000000147792889038100838661193847656值。

脚注

1 Java SE 10的规则可以在该toString(float d)部分的java.lang.float文档中找到。该double文档是相似的。段落中最相关的部分为粗体:

返回的字符串表示形式float argument。下面提到的所有字符都是ASCII字符。

  • 如果参数为NaN,则结果为字符串“ NaN”。

  • 否则,结果是一个字符串,代表参数的符号和大小(绝对值)。如果符号为负,则结果的第一个字符为' -'('\u002D'); 如果符号为正,则结果中不显示符号字符。至于大小m

    • 如果m为无穷大,则用字符“ Infinity”表示;因此,正无穷大产生结果“无穷大”,而负无穷大产生结果“-无穷大”。

    • 如果m为零,则用字符“ 0.0”表示;因此,负零将产生结果“ -0.0”,而正零将产生结果“ 0.0”。

    • 如果m大于或等于10 -3但小于10 7,则将其表示为m的整数部分,十进制形式,不带前导零,后跟' .'('\u002E'),后跟一个或多个十进制数字代表m的小数部分。

    • 如果m小于10 -3或大于或等于10 7,则以所谓的“计算机科学计数法”表示。令n为唯一整数,使10 nm <10 n +1 ; 然后设am和10 n的数学精确商,即1?一个然后<10的大小被表示为的整数部分一个,作为一个单一的十进制数字,后跟“ .”( '\u002E'),后面是表示小数部分十进制数字一个,后面跟有字母“E'('\u0045'),后跟n表示为十进制整数,由方法产生Integer.toString(int)

ma的小数部分必须打印多少个数字?必须至少有一个数字来表示小数部分,并且除此以外,还需要与唯一数量不同的数字,以便将参数值与type的相邻值唯一区分开float也就是说,假设x是此方法针对有限的非零参数f生成的十进制表示形式所表示的精确数学值。那么f必须是float最接近x的值;或者,如果两个float值相等地接近x,则f必须是其中之一,并且f的最低有效位必须为0。