我尝试了以下代码。但是使用BigDecimal减去时会得到不同的结果。
double d1 = 0.1;
double d2 = 0.1;
System.out.println("double result: "+ (d2-d1));
float f1 = 0.1F;
float f2 = 0.1F;
System.out.println("float result: "+ (f2-f1));
BigDecimal b1 = new BigDecimal(0.01);
BigDecimal b2 = new BigDecimal(0.01);
b1 = b1.subtract(b2);
System.out.println("BigDecimal result: "+ b1);
结果:
double result: 0.0
float result: 0.0
BigDecimal result: 0E-59
我还在努力。任何人都可以澄清。
答案 0 :(得分:11)
[这里有很多答案告诉你,二进制浮点不能完全代表0.01,并暗示你所看到的结果在某种程度上是不准确的。虽然第一部分是真的,但这不是真正的核心问题。]
答案是“0E-59” 等于0.回想一下BigDecimal
是未缩放值和小数比例因子的组合:
System.out.println(b1.unscaledValue());
System.out.println(b1.scale());
显示:
0
59
未缩放的值 0,如预期的那样。 “奇怪的”比例值只是0.01的非精确浮点表示的十进制扩展的伪像:
System.out.println(b2.unscaledValue());
System.out.println(b2.scale());
显示:
1000000000000000020816681711721685132943093776702880859375
59
下一个显而易见的问题是,为什么BigDecimal.toString
只显示b1
为“0
”,为方便起见?答案是字符串表示需要明确。来自Javadoc for toString
:
可区分的
BigDecimal
值与此转换的结果之间存在一对一的映射关系。也就是说,每个可区分的BigDecimal
值(未缩放的值和比例)在使用toString
时都具有唯一的字符串表示形式。如果使用BigDecimal
构造函数将该字符串表示转换回BigDecimal(String)
,则将恢复原始值。
如果它只显示“0
”,那么您将无法返回到这个确切的BigDecimal
对象。
答案 1 :(得分:7)
使用String中的构造函数:b1 = new BigDecimal("0.01");
(幻灯片23) http://strangeloop2010.com/system/talks/presentations/000/014/450/BlochLee-JavaPuzzlers.pdf
答案 2 :(得分:4)
有趣的是,值看起来是相等的,减法确实给你零,它似乎只是打印代码的问题。以下代码:
import java.math.BigDecimal;
public class Test {
public static void main(String args[]) {
BigDecimal b1 = new BigDecimal(0.01);
BigDecimal b2 = new BigDecimal(0.01);
BigDecimal b3 = new BigDecimal(0);
if (b1.compareTo(b2) == 0) System.out.println("equal 1");
b1 = b1.subtract(b2);
if (b1.compareTo(b3) == 0) System.out.println("equal 2");
System.out.println("BigDecimal result: "+ b1);
}
}
输出两条 equal
条消息,表示值 相同,并且在减去时零。< / p>
您可以尝试将其作为一个错误提出来,看看Oracle带来了什么。他们可能只会声明0e-59
仍为零,因此不是错误,或者BigDecimal documentation page上描述的相当复杂的行为正如预期的那样工作。具体而言,指出:
可区分的BigDecimal值与此转换的结果之间存在一对一的映射关系。也就是说,每个可区分的BigDecimal值(未缩放的值和比例)都具有使用toString的结果的唯一字符串表示形式。如果使用BigDecimal(String)构造函数将该字符串表示形式转换回BigDecimal,则将恢复原始值。
原始值需要可恢复的事实意味着toString()
需要为每个比例生成唯一字符串,这就是您获得0e-59
的原因。否则,将字符串转换回到BigDecimal
可能会给您一个不同的值(非标定值/比例元组)。
如果您确实希望零显示为“0”而不管比例如何,您可以使用以下内容:
if (b1.compareTo(BigDecimal.ZERO) == 0) b1 = new BigDecimal(0);
答案 3 :(得分:2)
您必须获得返回值:
BigDecimal b3 = b1.subtract(b2);
System.out.println("BigDecimal result: "+ b3);
答案 4 :(得分:2)
所以真正的问题是:使用以下代码,
BigDecimal b1 = new BigDecimal(0.01);
BigDecimal b2 = new BigDecimal(0.01);
b1 = b1.subtract(b2);
为什么b1.toString()
评估为"0E-59"
而不评估为"0.0"
,"0E0"
或仅"0"
?
原因是toString()
打印BigDecimal
的规范格式。有关详细信息,请参阅BigDecimal.toString()。
最后,0E-59
0.0
- 它是0*10^59
,在数学上评估为0.因此,意外结果是内部问题代表BigDecimal
。
要获取浮点值或双精度值,请使用
b1.floatValue());
或
b1.doubleValue());
两者都评估为0.0
。
答案 5 :(得分:2)
1.此构造函数的结果可能有些不可预测。有人可能会认为在Java中编写新的BigDecimal(0.1)会创建一个 BigDecimal正好等于0.1(未缩放的值为1,带有 比例为1),但它实际上等于 0.1000000000000000055511151231257827021181583404541015625。这是因为0.1不能完全表示为double(或者为此) 重要的是,作为任何有限长度的二进制分数)。因此,价值 传入构造函数的不完全等于 0.1,尽管有外表。
2.另一方面,String构造函数是完全可预测的:编写新的BigDecimal(“0.1”)会创建一个BigDecimal,它正是 等于0.1,正如人们所期望的那样。因此,一般来说 建议首先使用String构造函数 之一。
3.当必须将double用作BigDecimal的源时,请注意此构造函数提供了精确的转换;它没有给出 使用。将double转换为String的结果相同 Double.toString(double)方法然后使用BigDecimal(String) 构造函数。要获得该结果,请使用静态valueOf(double) 方法
答案 6 :(得分:1)
这是一个已知的问题,BigDecimal(double val)API 这个构造函数的结果可能有些不可预测。虽然它在这个插曲中看起来真的很奇怪。实际原因是新的BigDecimal(0.01)产生具有近似值
的BigDecimal0.01000000000000000020816681711721685132943093776702880859375
具有较长的精度,因此减法的结果也具有较长的精度。
无论如何,我们可以通过这种方式解决“问题”
BigDecimal b1 = new BigDecimal("0.01");
BigDecimal b2 = new BigDecimal("0.01");
或者我们可以使用构造函数来设置精度
BigDecimal b1 = new BigDecimal(0.01, new MathContext(1));
BigDecimal b2 = new BigDecimal(0.01, new MathContext(1));
答案 7 :(得分:1)
像这样使用:
BigDecimal b1 = BigDecimal.valueOf(0.01);
BigDecimal b2 = BigDecimal.valueOf(0.01);
b1 = b1.subtract(b2);
System.out.println("BigDecimal result: "+ b1);