使用浮点数和双精度数进行计算,结果不同。

3
我正在处理一个涉及8位小数点计算物品价格的项目。 但有时计算结果会不同。
我尝试了一些样例,但得到了不同的计算结果:
    public static void main(String[] args) {
        double value1 =  0.999939f * 0.987792f;
        double value2 =  0.999939 * 0.987792;
        double value3 =  (double)0.999939f * 0.987792f;     
        System.out.println("value 1 : "+ value1);
        System.out.println("value 2: "+ value2);
        System.out.println("value 3 : "+ value3);
    }

输出结果为:

value 1 : 0.9877317547798157
value 2 : 0.9877317446880001
value 3 : 0.9877317839126931

这三个结果是不同的。

我有些困惑。请问有人能够解释一下这里发生了什么吗?
谢谢。

我已经查看了一些已经回答的内容,但是我只想知道一些只使用浮点数和双精度浮点数计算的方法。否则我需要更改很多地方,这会很痛苦。


2
不要使用double或float类型的变量来表示货币金额。这样会导致很多与舍入等相关的问题。应该使用BigDecimal类代替。 - user784540
感谢 @RafaelOsipov。我还有一些疑问。我们能够固定小数点的位数吗?这会影响计算和四舍五入吗?Java 提供了哪些 API 可以提供帮助? - Sonu Gupta
@RealSkeptic 这个链接讲述了为什么我们不应该使用double或float来表示。它非常有帮助,下次我不会再使用float和doubles了。但是现在我的问题是:“我能否做一些关于float和doubles的事情,而不会影响结果?” - Sonu Gupta
@RafaelOsipov已经在他的评论中回答了这个问题,而我提供的链接提供了其他可能性,比如使用intlong(在你的情况下,long会更好,因为你有很多小数位)。 - RealSkeptic
具有8位小数精度的BigDecimal适用于精确表示小数点后8位的十进制分数。您可以控制舍入模式,但其中一个选项与浮点数相同。 - Patricia Shanahan
我不认为这是那两个问题的重复(它们可能有关联,但它们并没有回答这个特定的问题)。 - Simon Byrne
2个回答

3
由于浮点数和十进制数的表示方式不同,造成强制类型转换可能导致不同的结果。
对于:double value1 = 0.999939f * 0.987792f ,你将两个浮点数相乘,然后将其转换为双精度浮点型。得到的浮点数表示正在转换为双精度浮点数表示。
对于:double value2 = 0.999939 * 0.987792; ,你正在将两个双精度浮点数相乘并将其保存在双精度浮点数中。这一次没有进行类型转换,因此表示永远不会改变(即没有由于数据表示的更改而可能导致的数据丢失)。
对于:double value3 = (double)0.999939f * 0.987792f; ,你正在将从浮点数转换为双精度浮点数的双精度浮点数乘以一个浮点数。由于Java数学的工作方式,第二个浮点数很可能也被转换为双精度浮点数,因此现在你正在将两个曾经是浮点数的双精度浮点数相乘,导致产生第三组表示。
由于浮点数和双精度浮点数具有不同的精度,因此每个变量将得到不同的结果。有关浮点算术的更多信息,请参见此处

为什么你说涉及到双精度浮点数时就没有潜在的数据丢失?这显然是不正确的。 - Amit
@Amit,你说得对,当数字以Double的形式记录并在乘法和保存时有可能丢失数据,但这都是Double工作定义内的。我的意思是更改表示不会导致数据丢失。我会进行编辑以说明这一点。 - Evan Frisch

1
当您在数字后面加上"f"字符时,它会被视为float,表示它被编码为32位,而没有"f"字符时,它是一个double,编码为64位。
比特数越多,您将更准确地表示十进制数字。这对于小数点后面有几个数字的数字没有影响,但在您的情况下,它非常重要。
因此,在您的代码中,仅使用double变量。

这不准确。浮点数表示(单精度或双精度)的精度是有限的,无论小数位数如何。实际上,自然数同样可能不准确。 - Amit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接