我知道,由于浮点数/双浮点数的性质,不应该将它们用于精确的重要计算。然而,由于对类似问题的混合答案,我对它们的局限性有点困惑,无论浮点数和双倍数是否总是不准确,而不管有效数字是多少,或者只不准确到第16位。
我用Java运行了几个示例,
System.out.println(Double.parseDouble("999999.9999999999");
// this outputs correctly w/ 16 digits
System.out.println(Double.parseDouble("9.99999999999999");
// This also outputs correctly w/ 15 digits
System.out.println(Double.parseDouble("9.999999999999999");
// But this doesn't output correctly w/ 16 digits. Outputs 9.999999999999998
我找不到另一个答案的链接,该答案指出,1.98和2.02等值将四舍五入为2.0,因此会产生误差,但测试表明,这些值打印正确。因此,我的第一个问题是,浮点/双精度值是否总是不准确,或者是否有一个可以确保精度的下限。
我的第二个问题是关于使用BigDecimal。我知道我应该使用BigDecimal进行精度重要的计算。因此,我应该使用BigDecimal的方法进行算术和比较。但是,BigDecimal还包括将BigDecimal转换为double的doubleValue()
方法。对我来说,比较两个肯定少于16位的值安全吗?根本不会对它们进行运算,因此固有值不应该发生变化。
例如,我这样做安全吗?
BigDecimal myDecimal = new BigDecimal("123.456");
BigDecimal myDecimal2 = new BigDecimal("234.567");
if (myDecimal.doubleValue() < myDecimal2.doubleValue()) System.out.println("myDecimal is smaller than myDecimal2");
编辑:在阅读了一些对我自己答案的回复后,我意识到我的理解是错误的,并将其删除。下面是其中的一些片段,将来可能会有所帮助。
“一个双倍不能精确地保持0.1。最接近0.1的可表示值是0.1000000000000000055511151231257827021181583404541015625。JavaDouble.toString只打印足够的数字来唯一识别双精度,而不是确切的值。"-Patricia Sharahan
资料来源:
https://stackoverflow.com/a/5749978-表示双精度计数器最多可容纳15位数字
顺便说一句,(也许微不足道)存储已知精度值的一个好而可靠的方法是简单地将其乘以相关因子,并存储为完全精确的整型。
例如:
double costInPounds = <something>; //e.g. 3.587
int costInPence = (int)(costInPounds * 100 + 0.5); //359
很明显,一些精度可能会丢失,但是如果已知所需/期望的精度,这可以省去浮点值的很多麻烦,一旦这样做了,进一步的操作就不会丢失精度。
0.5
用于确保四舍五入按预期工作(int)
取所提供的双精度
值的“下限”,因此添加0.5使其按预期向上和向下取整。
我建议你阅读这一页:
https://en.wikipedia.org/wiki/Double-precision_floating-point_format
一旦您阅读并理解了它,并且可能将几个示例转换为64位浮点格式的二进制表示形式,那么您就可以更好地了解双精度浮点可以包含哪些有效数字。
问题内容: java中双值的乘法运算符的保证精度是多少? 例如,2.2 * 100是220.00000000000003,但是220是双精度数。220.00000000000003是220之后的下一个两倍。 问题答案: 乘法工作正常,但不能精确表示为双精度。最接近的双打是: 2.199999999999999733(0x4001999999999999) 2.200000000000000177(
问题内容: 我想知道为什么会出现此错误。(这是Eclipse调试的显示日志) 我不明白为什么我没有得到2.97! 问题答案: 如果需要,您应该已经习惯了。 s以分数形式存储在 二进制 而不是十进制中。因此,例如,仅存储为。 并且不能精确地表示为二进制分数,因此会有一些舍入误差。 您可能还会发现本文很有帮助。
问题内容: 使用double时,如何使用模数运算符处理Java的怪异行为? 例如,你所期望的结果是(事实上,谷歌说,我不是要疯了),但是当我在Java中,我得到运行它。 我了解这是Java存储和处理方式加倍的结果,但是有没有解决的方法? 问题答案: 如果需要精确的结果,请使用精确的类型: 为什么是3.8000 … 003?因为Java使用FPU来计算结果。3.9不可能以IEEE双精度表示法精确存储
问题内容: 我正在编写一个简单的Java程序。我需要从输入中获取一个字符串并将其分为两部分:1-double 2-string。然后,我需要对double进行简单的计算,并将结果发送到具有特定精度的输出(4)。它工作正常,但是当输入为0时出现问题,则不能正常工作。 例如,对于这些输入,输出将是: 1公斤 输出:2.2046 3.1公斤 输出:6.8343 但是当输入为0时,输出应为0.0000,但
在64位jvm上,还是这样吗?由于这些值现在可以放入单个地址空间,这是否意味着这些类型现在存储在单个地址空间中? 如果需要在这些类型上使用volatile以确保每个单独的地址空间不会无序更新,那么这是否会对线程产生影响?
什么是“精度”?使用float和double时,单和双有什么区别?“单精度32位IEEE 754浮点”“双精度64位IEEE 754浮点”是什么意思?