当前位置: 首页 > 知识库问答 >
问题:

为什么java运算中有些双精度除法是正确的?

艾弘义
2023-03-14

我理解二进制数的理论,所以双精度数的运算是不精确的。但是,在java中,我不知道为什么“(double)65 / 100”是0.65,这在十进制数上是完全正确的,除了0.6500000000004。

double a = 5;
double b = 4.35;
int c = 65;
int d = 100;
System.out.println(a -  b); // 0.6500000000000004
System.out.println((double) c /  d); // 0.65

共有1个答案

郑俊弼
2023-03-14

爪哇岛 完全搞砸了 有自己处理浮点二进制到十进制转换的方法。

C语言中的一个简单程序(使用gcc编译)给出了结果:

printf("1: %.20f\n", 5.0 - 4.35);         // 0.65000000000000035527
printf("2: %.20f\n", 65./100);            // 0.65000000000000002220

虽然Java给出了结果(注意你只需要17位数字就可以看到它,但我想让它更清楚):

System.out.printf("%.20f\n", 5.0 - 4.35); // 0.65000000000000040000
System.out.printf("%.20f\n", 65./100);    // 0.65000000000000000000

但是当使用%a格式说明符时,两种语言printf底层十六进制(正确)值:0x1.4cccccccccd000000p-1

<罢工> 因此,Java在代码中的某个位置执行了一些非法舍入。 这里明显的问题是,Java有一套不同的规则来将二进制转换为十进制,从Java规范:

m或a的小数部分的结果中的位数等于精度。如果未指定精度,则默认值为6。如果精度小于Float.toString(float)或Double.toString(double)分别返回的字符串中小数点后出现的位数,则将使用round half up算法对值进行舍入。否则,可能会附加零以达到精度。对于该值的规范表示形式,请根据需要使用Float.toString(float)或Double.toString(double)。(强调我的)

toString规范中:

m或a的小数部分必须打印多少位数?必须至少有一位数字来表示小数部分,除此之外,还必须有尽可能多的数字来唯一区分参数值和相邻的double类型值。即假设x是用这种方法对一个有限的非零自变量d产生的十进制表示所表示的精确的数学值,那么d一定是最接近x的double值;或者,如果两个double值同样接近x,则d必须是其中之一,并且d的有效数位的最低有效位必须是0。(强调我的)

因此,Java确实执行了不同于C的二进制到十进制转换,但它比任何其他值都更接近真实的二进制值,因此规范保证二进制值可以通过十进制到二进制的转换恢复。

William Kahan教授在本文中警告了一些Java浮点问题:

Java的浮点运算是如何伤害所有人的

但这种转换行为似乎是IEEE的抱怨。

编辑:我在评论中包含了@MarkDickinson提供的信息,以报告这种Java行为,尽管不同于C,但已记录在案,并且符合IEEE。这已经在这里、这里和这里进行了解释。

 类似资料:
  • 什么是“精度”?使用float和double时,单和双有什么区别?“单精度32位IEEE 754浮点”“双精度64位IEEE 754浮点”是什么意思?

  • IEEE 754浮点数是离散的。 查看此代码在IdeOne上的实时运行。通用域名格式。在JDK8中运行时,输出是 浮点值按预期打印。我预计双倍值0.1d会像1.000000000000000055511151231260一样打印。为什么它在分数部分打印所有零? 如果我把双变量d转换成字符串,它会输出0.1。 java如何将0.1d(大约为1.0000000 149011938476565E-1)的

  • 问题内容: java中双值的乘法运算符的保证精度是多少? 例如,2.2 * 100是220.00000000000003,但是220是双精度数。220.00000000000003是220之后的下一个两倍。 问题答案: 乘法工作正常,但不能精确表示为双精度。最接近的双打是: 2.199999999999999733(0x4001999999999999) 2.200000000000000177(

  • 问题内容: 当我使用双精度数在Java中将317除以219时,得到1。 例如: 输出为:1。 这是因为它是重复的小数吗?不得不使用BigDecimal代替,这很烦人。 问题答案: 试试这个 java中编码数字的默认类型是,因此使用现有的代码,java正在使用两个数字,并且除法的结果也将是,这将截断小数部分以得出最终结果。然后将此结果从转换为,而没有编译器警告,因为它是 扩大的转换 (保证源类型“适

  • 对于实现精确 IEEE 754 算术的 C99 编译器,是否存在型的 、的值,使得 ? 编辑:所谓“实现精确的IEEE754算法”,我指的是一个正确地将FLT_EVAL_METHOD定义为0的编译器。 提供符合IEEE 754标准的浮点数的C编译器只能将单精度除法替换为常数,如果所述逆本身可以完全表示为。 实际上,这种情况只发生在2的幂上。因此,程序员Alex可能确信< code>f / 2.0f

  • 问题内容: 我正在编写一个简单的Java程序。我需要从输入中获取一个字符串并将其分为两部分:1-double 2-string。然后,我需要对double进行简单的计算,并将结果发送到具有特定精度的输出(4)。它工作正常,但是当输入为0时出现问题,则不能正常工作。 例如,对于这些输入,输出将是: 1公斤 输出:2.2046 3.1公斤 输出:6.8343 但是当输入为0时,输出应为0.0000,但