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

在Java中使用double vs BigDecimal解析双值,java,floating-point,double,Java,Floating Point,Double,我是一个有经验的开发人员,但不是数学专家。我对IEEE浮点规范了解得足够多,以至于我害怕对解析、打印和比较它们做出假设 我知道我可以使用从字符串解析double。我知道我也可以使用将同一字符串解析为BigDecimal,然后使用BigDecimal请求double 我浏览了这两种技术的API和代码,似乎BigDecimal有很多不同的解析和转换选项 对于所有字符串输入,是否保证两种技术(Double.parseDouble)和new bigdecim.doubleValue())都能产生完全相同

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

我知道我可以使用从
字符串
解析
double
。我知道我也可以使用将同一字符串解析为
BigDecimal
,然后使用
BigDecimal
请求
double

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


对于所有字符串输入,是否保证两种技术(
Double.parseDouble)
new bigdecim.doubleValue()
)都能产生完全相同的
Double
原语值,前提是该值不在大多数输入值的正负
Double.MAX\u Double
范围之外,这两种技术应产生相同的值。虽然他们可能不会,但似乎不太可能

各国:

API注释:

对于
float
double
NaN和±无穷大以外的值,此构造函数与
float.toString(float)
double.toString(double)
返回的值兼容

然而,缔约国:

返回一个新的
double
初始化为指定的
字符串所表示的值,由类
double
valueOf
方法执行

然后描述了该方法所接受的格式

让我们测试一下

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

public static void main(String[] args)
{
    String[] values = {"0", "0.1", "0.33333333333333333333", "-0", "-3.14159265", "10.1e100",
            "0.00000000000000000000000000000000000000000000000000142857142857",
            "10000000000.000000000000000001", "2.718281828459",
            "-1.23456789e-123", "9.87654321e+71", "66666666.66666667",
            "1.7976931348623157E308", "2.2250738585072014E-308", "4.9E-324",
            "3.4028234663852886E38", "1.1754943508222875E-38", "1.401298464324817E-45",
            String.valueOf(Math.E), String.valueOf(Math.PI), String.valueOf(Math.sqrt(2))
    };
    for (String value : values) {
        System.out.println(isDoubleEqual(value));
    }
}
// Test if the representations yield the same exact double value.
public static boolean isDoubleEqual(String s) {
    double d1 = Double.parseDouble(s);
    double d2 = new BigDecimal(s).doubleValue();
    return d1 == d2;
}
对于这些值,我得到所有
true
s。这并非详尽无遗,因此对于所有可能的
double
值,很难证明它是正确的。只需一个
false
即可显示反例。然而,这似乎证明了所有合法的
double
字符串表示都是正确的

public static void main(String[] args)
{
    String[] values = {"0", "0.1", "0.33333333333333333333", "-0", "-3.14159265", "10.1e100",
            "0.00000000000000000000000000000000000000000000000000142857142857",
            "10000000000.000000000000000001", "2.718281828459",
            "-1.23456789e-123", "9.87654321e+71", "66666666.66666667",
            "1.7976931348623157E308", "2.2250738585072014E-308", "4.9E-324",
            "3.4028234663852886E38", "1.1754943508222875E-38", "1.401298464324817E-45",
            String.valueOf(Math.E), String.valueOf(Math.PI), String.valueOf(Math.sqrt(2))
    };
    for (String value : values) {
        System.out.println(isDoubleEqual(value));
    }
}
// Test if the representations yield the same exact double value.
public static boolean isDoubleEqual(String s) {
    double d1 = Double.parseDouble(s);
    double d2 = new BigDecimal(s).doubleValue();
    return d1 == d2;
}
我还尝试了前导空格,例如
“4”
BigDecimal(String)
构造函数抛出了一个
NumberFormatException
Double.parseDouble
正确地修剪了输入

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

如果包含这些边缘情况,则一个方法可能会引发另一个方法不会引发的异常。如果您正在寻找范围内有限值的正常基数10字符串,答案是“似乎有可能”

对于所有字符串输入,是否保证两种技术(
Double.parseDouble)
new BigDecimal.doubleValue()
)都能产生完全相同的双原语值,前提是该值不在正负
Double.MAX\u Double
的范围之外

不,当然不是

首先,有一些字符串输入
Double.parseDouble
支持,但
newbigdecimal
不支持(例如,十六进制文字)

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


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

文档没有具体说明这一点,因此您必须查看源代码。我会自己做,但我现在不在我的工作站上。在JDK 13中,
BigDecimal#doubleValue
实际上调用了
Double#parseDouble
如果“这个BigDecimal的有效位的绝对值大于Long.MAX\u值”。这是取自
BigDecimal
intCompact
字段上的javadoc。技术上的一个区别是
新的BigDecimal(null)。doubleValue()
是一个编译问题,而
Double.parseDouble(null)
是一个
NullPointerException
,但仍然可以编译。这仅仅是因为
bigdecimic
被重载,所以
null
没有被转换为
字符串。我不认为你会在这里得到保证,但很有可能。但是它们使用不同的方式来解析字符串(
readJavaFormatString#readJavaFormatString(String)
BigDecimal(char[],int,int-MathContext)
)。尽管它们的工作原理似乎相似,但它们可能会在未来的版本中发生变化,从而导致结果的差异。
Double
的Javadoc精确地描述了通过对
字符串调用
parseDouble
得到的
Double
值。
BigDecimal
的Javadoc不精确,因此无法保证您得到的
double
值是多少。我认为这是JavaDoc在<代码> BigDecimal <代码>中的一个疏忽,我希望Oracle在将来的版本中改进它。BigDePiple也缺少无限和NANS。@ PATRICIASHANAHAN:正确;这些属于“支持
Double.parseDouble
new BigDecimal
不支持的字符串输入”。由于OP指定“该值不在正负
Double.MAX\u Double
的范围之外”,因此我认为单独调用这些值没有意义。我建议将它们列出,以供将来的读者使用。我同意质询中所述的限制