Java中double转BigDecimal的注意事项
先上结论:不要直接⽤double变量作为构造BigDecimal的参数。
线上有这么⼀段Java代码逻辑:
1,接⼝传来⼀个JSON串,⾥⾯有个数字:57.3。
2,解析JSON并把这个数字保存在⼀个float变量。
3,把这个float变量赋值给⼀个 BigDecimal对象,⽤的是BigDecimal的double参数的构造:
new BigDecimal(double val)
4,把这个BigDecimal保存到MySQL数据库,字段类型是decimal(15,2)。
这段代码逻辑在线上跑了好久了,数据库保存的值是57.3也没什么问题,但是在今天debug的时候发现,第三步的BigDecimal对象保存的值并不是57.3,⽽是57.299999237060546875,很明显,出现了精度的问题。
⾄于数据库最终保存了正确的57.3完全是因为字段类型设置为2位⼩数,超过2位⼩数就四舍五⼊,所以
才得到了正确的结果,相当于MySQL给我们把这个精度问题掩盖了。
总觉得这是个坑,所以研究了⼀下相关的知识。
⾸先是BigDecimal的double参数构造,在官⽅JDK⽂档中对这个构造是这么描述的:
public BigDecimal(double val)
Translates a double into a BigDecimal which is the exact decimal representation of the double's binary floating-point value. The scale of the returned BigDecimal is the smallest value such that (10scale × val) is an integer.
Notes:
The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithst
anding.
The String constructor, on the other hand, is perfectly predictable: writing new BigDecimal("0.1") creates a BigDecimal which is exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the String constructor be used in preference to this one.
When a double must be used as a source for a BigDecimal, note that this constructor provides an exact conversion; it does not give the same result as converting the double to a String using String(double) method and then using the BigDecimal(String) constructor. To get that result, use the static valueOf(double) method.
Parameters:
val - double value to be converted to BigDecimal.
bigdecimal转换为integerThrows:
NumberFormatException - if val is infinite or NaN.
翻译⼀下⼤概是这样的:
1,BigDecimal(double val)构造,⽤double当参数来构造⼀个BigDecimal对象。
2,但是这个构造不太靠谱(unpredictable),你可能以为BigDecimal(0.1)就是妥妥的等于0.1,但是你以为你以为的就是你以为的?还真不是,BigDecimal(0.1)这货实际上等于0.1000000000000000055511151231257827021181583404541015625,因为准确的来说0.1本⾝不能算是⼀个double(其实0.1不能代表任何⼀个定长⼆进制分数)。
3,BigDecimal(String val)构造是靠谱的,BigDecimal(“0.1”)就是妥妥的等于0.1,推荐⼤家⽤这个构造。
4,如果你⾮得⽤⼀个double变量来构造⼀个BigDecimal,没问题,我们贴⼼的提供了静态⽅法valueOf(double),这个⽅法跟new String(double))效果是⼀样的。
说⽩了就是别直接拿double变量做参数,最好使⽤String类型做参数或者使⽤静态⽅法valueOf(double),我写了个例⼦试了⼀下:
public static void main(String[] args) {
float a=57.3f;
BigDecimal decimalA=new BigDecimal(a);
System.out.println(decimalA);
double b=57.3;
BigDecimal decimalB=new BigDecimal(b);
System.out.println(decimalB);
double c=57.3;
BigDecimal decimalC=new String(c));
System.out.println(decimalC);
double d=57.3;
BigDecimal decimalD=BigDecimal.valueOf(d);
System.out.println(decimalD);
}
输出结果:
57.299999237060546875
57.2999999999999971578290569595992565155029296875
57.3
57.3
以后还是尽量按照官⽅推荐的套路来,否则不知道什么时候⼜给⾃⼰挖坑了。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。