在Java中使用Double vs BigDecimal解析double值

Gar*_*son 16 java floating-point double

我是一位经验丰富的开发人员,但不是数学专家。我对IEEE浮点规范了解得足够多,因此害怕做出关于解析,打印和比较它们的假设。

我知道我可以doubleStringusing中解析一个Double.parseDouble(String s)。我知道我还可以分析相同的字符串转换成BigDecimal使用new BigDecimal(String s),然后要求BigDecimaldouble使用BigDecimal.doubleValue()

我浏览了这两种技术的API和代码,似乎BigDecimal有很多不同的解析和转换选项。

如果所有的字符串输入都不在正负范围之内,是否保证两种技术(Double.parseDouble(s)new BigDecimal(s).doubleValue())都能产生完全相同的double原始值Double.MAX_DOUBLE

rge*_*man 5

对于大多数输入值,两种技术应产生相同的值。虽然他们仍然有可能不会,但看起来不太可能。

\n

构造函数BigDecimal(String)Javadocs指出:

\n
\n

API注意事项:

\n

float对于和NaN 和 \xc2\xb1Infinity以外的值,此构造函数与和double返回的值兼容。Float.toString(float)Double.toString(double)

\n
\n

然而,该Double.parseDouble(String)方法指出:

\n
\n

返回一个新double值,该值被初始化为指定的值,由类的方法String执行。valueOfDouble

\n
\n

接下来描述该方法接受的格式。

\n

让我们测试一下!

\n

让我们测试一些值。彻底测试这一点看起来需要付出巨大的努力,但让我们测试一下,包括一些字符串值,这些值表示已知会产生浮点错误或不精确表示的值。

\n
public static void main(String[] args)\n{\n    String[] values = {"0", "0.1", "0.33333333333333333333", "-0", "-3.14159265", "10.1e100",\n            "0.00000000000000000000000000000000000000000000000000142857142857",\n            "10000000000.000000000000000001", "2.718281828459",\n            "-1.23456789e-123", "9.87654321e+71", "66666666.66666667",\n            "1.7976931348623157E308", "2.2250738585072014E-308", "4.9E-324",\n            "3.4028234663852886E38", "1.1754943508222875E-38", "1.401298464324817E-45",\n            String.valueOf(Math.E), String.valueOf(Math.PI), String.valueOf(Math.sqrt(2))\n    };\n    for (String value : values) {\n        System.out.println(isDoubleEqual(value));\n    }\n}\n// Test if the representations yield the same exact double value.\npublic static boolean isDoubleEqual(String s) {\n    double d1 = Double.parseDouble(s);\n    double d2 = new BigDecimal(s).doubleValue();\n    return d1 == d2;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

对于这些值,我得到了所有trues。这绝不是详尽无遗的,因此很难证明对于所有可能的double值都是正确的。只需要举出一个false反例即可。然而,这似乎是一些证据,表明它对于所有合法的double字符串表示都是正确的。

\n

我还尝试过前导空格,例如" 4". 构造BigDecimal(String)函数抛出了 aNumberFormatExceptionDouble.parseDouble正确地修剪了输入。

\n

构造函数BigDecimal(String)不会接受Infinityor NaN,但您只询问正常的有限范围。该Double.parseDouble方法接受十六进制浮点表示,但BigDecimal(String)不接受。

\n

如果包含这些边缘情况,一种方法可能会引发异常,而另一种方法不会引发异常。如果您正在寻找范围内有限值的普通以 10 为基数的字符串,答案是“看起来有可能”。

\n


rua*_*akh 5

对于所有字符串输入,这两种技术(Double.parseDouble(s)new BigDecimal(s).doubleValue())是否保证生成完全相同的双精度原始值(前提是该值不超出 plus 或 minus 的范围)Double.MAX_DOUBLE

不,当然不是。

一方面,有一些字符串输入Double.parseDouble(s)支持但new BigDecimal(s)不支持(例如,十六进制文字)。

另一方面,Double.parseDouble("-0")产生零,而new BigDecimal("-0").doubleValue()产生零(因为BigDecimal没有带符号零的概念)。

虽然与你的问题没有直接关系,但为了其他读者的利益,我被要求指出Double.parseDouble(s)支持 NaN 和无穷大,而不支持 NaN 和无穷BigDecimal大。

  • BigDecimal 还缺少无穷大和 NaN。 (2认同)
  • 我建议为未来的读者列出它们。我同意问题中所述的限制不包括无穷大和 NaN。 (2认同)