Bigdecimal 传一个 String类型的数据和dubbo类型的数据得到的值一样吗?为什么
不一样
这里特别说明一下BigDecimal类的两个构造函数的区别,他们分别是:
new BigDecimal(String val ) 和 new BigDecimal(double val )
解析-1:
System.out.println(new BigDecimal(0.1).toString()); // 0.1000000000000000055511151231257827021181583404541015625
System.out.println(new BigDecimal("0.1").toString()); // 0.1
System.out.println(new BigDecimal(Double.toString(0.1000000000000000055511151231257827021181583404541015625)).toString());// 0.1
System.out.println(new BigDecimal(Double.toString(0.1)).toString()); // 0.1
分析一下上面代码的问题(注释的内容表示此语句的输出)
十进制小数0.1会转换成二进制,因double有效位数为 16位这就会出现存储小数位数不够的情况,这种情况下就会出现误差
第一行:事实上,由于二进制无法精确地表示十进制小数0.1,但是编译器读到字符串"0.1"之后,
必须把它转成8个字节的double值,因此,编译器只能用一个最接近的值来代替0.1了,
即0.1000000000000000055511151231257827021181583404541015625。因此,在运行时,
传给BigDecimal构造函数的真正的数值是0.1000000000000000055511151231257827021181583404541015625。
第二行:BigDecimal能够正确地把字符串转化成真正精确的浮点数。
第三行:问题在于Double.toString会使用一定的精度来四舍五入double,然后再输出。
所以Double.toString(0.1000000000000000055511151231257827021181583404541015625)输出的事实上是"0.1",
因此生成的BigDecimal表示的数也是0.1。
第四行:基于前面的分析,事实上这一行代码等价于第三行
结论:
1.如果你希望BigDecimal能够精确地表示你希望的数值,那么一定要使用字符串来表示小数,
并传递给BigDecimal的构造函数。
2.如果你使用Double.toString来把double转化字符串,然后调用BigDecimal(String),这个也是不靠谱的,
它不一定按你的想法工作。
3.如果你不是很在乎是否完全精确地表示,并且使用了BigDecimal(double),那么要注意double本身的特例,
double的规范本身定义了几个特殊的double值(Infinite,-Infinite,NaN),不要把这些值传给BigDecimal,
否则会抛出异常。
解析-2:
package archie2010;
import java.math.BigDecimal;
import java.math.MathContext;
public class BigDecimalTest {
/**
* @param args
* @reference archie2010
* @function 实现将double类型的值转换为BigDecimal类型的值的不同途径以及各途径间的区别
* 一:有人可能认为在 Java 中写入 new BigDecimal(0.1) 所创建的 BigDecimal 正好等于 0.1
(非标度值 1,其标度为 1),
* 但是它实际上等于 0.1000000000000000055511151231257827021181583404541015625。
* 这是因为 0.1 无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。
* 这样,传入 到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。
* 二:另一方面,String 构造方法是完全可预知的:写入 new BigDecimal("0.1") 将创建一个 BigDecimal,
* 它正好 等于预期的 0.1。因此,比较而言,通常建议优先使用 String 构造方法。
* 三:使用public static BigDecimal valueOf(double val)
* 使用 Double.toString(double) 方法提供的 double 规范的字符串表示形式将 double 转换为 BigDecimal。
* 这通常是将 double(或 float)转化为 BigDecimal 的首选方法,
* 因为返回的值等于从构造 BigDecimal(使用 Double.toString(double) 得到的结果)得到的值。
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
double d1,d2;
d1 = 0.10334;
d2 = 1234.0;
System.out.println("直接输出double类型的值:");
printDouble(d1, d2);
System.out.println("将double类型直接转换为BigDecimal类型,再输出:");
printDoubleToBigDecimal(d1, d2);
System.out.println("将double类型转换为String类型,再转换为BigDecimal类型,再输出:");
printDoubleToStrToBigDecimal(d1, d2);
System.out.println("使用静态方法valueOf将double转换为BigDecimal类型并输出:");
printDoubleToBigDecimalWithValueof(d1,d2);
System.out.println("直接将double类型值转换为确定精度为3的BigDecimal类型值:");
printDoubleToBigDecimalWithPrecision(d1, d2, 3);
System.out.println("将double类型值转换为String类型再转换为确定精度(3)的BigDecimal类型值:");
printDoubleToBigDecimalWithPrecisionAsStr(d1,d2,3);
}
/**
* @param v1:double类型;
* @param v2:double类型;
* @return
* */
public static void printDouble(double v1, double v2){
System.out.println("d1="+v1);
System.out.println("d2="+v2);
输出结果:
d1=0.10334 d2=1234.0
}
/**
* @param v1:double类型;
* @param v2:double类型;
* @return
* @Constructor public BigDecimal(double val)
* 将 double 转换为 BigDecimal,后者是 double 的二进制浮点值准确的十进制表示形式。
* 返回的 BigDecimal 的标度是使 (10scale × val) 为整数的最小值。
* */
public static void printDoubleToBigDecimal(double v1, double v2){
BigDecimal d1TobigDe = new BigDecimal(v1);
BigDecimal d2TobigDe = new BigDecimal(v2);
System.out.println("d1TobigDe="+d1TobigDe);
System.out.println("d2TobigDe="+d2TobigDe);
输出结果:
d1TobigDe=0.10334000000000000130118138486068346537649631500244140625
d2TobigDe=1234
}
/**
* @param v1:double类型;
* @param v2:double类型;
* @return
* @Constructor public BigDecimal(String val)
* 将 BigDecimal 的字符串表示形式转换为 BigDecimal。
* 字符串表示形式由可选符号 '+' ('\u002B') 或 '-' ('\u002D') 组成,
* 后跟零或多个十进制数字(“整数”)的序列,可以选择后跟一个小数,也可以选择后跟一个指数。
* */
public static void printDoubleToStrToBigDecimal(double v1, double v2){
BigDecimal d1ToStrToBigDe = new BigDecimal(String.valueOf(v1));
BigDecimal d2ToStrToBigDe = new BigDecimal(String.valueOf(v2));
System.out.println("d1ToStrToBigDe="+d1ToStrToBigDe);
System.out.println("d2ToStrToBigDe="+d2ToStrToBigDe);
输出结果:
d1ToStrToBigDe=0.10334
d2ToStrToBigDe=1234.0
}
/**
* @param v1:double类型;
* @param v2:double类型;
* @return
* @Constructor public static BigDecimal valueOf(double val)
* */
public static void printDoubleToBigDecimalWithValueof(double v1, double v2){
System.out.println("BigDecimal.valueOf(d1)="+BigDecimal.valueOf(v1));
System.out.println("BigDecimal.valueOf(d2)="+BigDecimal.valueOf(v2));
输出结果:
BigDecimal.valueOf(d1)=0.10334
BigDecimal.valueOf(d2)=1234.0
}
/**
* @param v1:double类型;
* @param v2:double类型;
* @return
* @Constructor public BigDecimal(double val,MathContext mc)
* */
public static void printDoubleToBigDecimalWithPrecision(double v1, double v2, int setPrecision){
BigDecimal d1ToBigDeWithPre = new BigDecimal(v1, new MathContext(setPrecision));
BigDecimal d2ToBigDeWithPre = new BigDecimal(v2, new MathContext(setPrecision));
System.out.println("d1ToBigDeWithPre="+d1ToBigDeWithPre);
System.out.println("d2ToBigDeWithPre="+d2ToBigDeWithPre);
输出结果:
d1ToBigDeWithPre=0.103
d2ToBigDeWithPre=1.23E+3
}
/**
* @param v1:double类型;
* @param v2:double类型;
* @return
* @Constructor public BigDecimal(String val,MathContext mc)
* 将 BigDecimal 的字符串表示形式转换为 BigDecimal,
* 接受与 BigDecimal(String) 构造方法相同的字符串(按照上下文设置进行舍入)。
* */
public static void printDoubleToBigDecimalWithPrecisionAsStr(double v1, double v2, int setPrecision){
BigDecimal d1ToStrToBigDeWithPre = new BigDecimal(String.valueOf(v1), new MathContext(setPrecision));
BigDecimal d2ToStrToBigDeWithPre = new BigDecimal(String.valueOf(v2), new MathContext(setPrecision));
System.out.println("d1ToStrToBigDeWithPre="+d1ToStrToBigDeWithPre);
System.out.println("d2ToStrToBigDeWithPre="+d2ToStrToBigDeWithPre);
输出结果:
d1ToStrToBigDeWithPre=0.103
d2ToStrToBigDeWithPre=1.23E+3
}
}
运行结果:
直接输出double类型的值:
d1=0.10334
d2=1234.0
将double类型直接转换为BigDecimal类型,再输出:
d1TobigDe=0.10334000000000000130118138486068346537649631500244140625
d2TobigDe=1234
将double类型转换为String类型,再转换为BigDecimal类型,再输出:
d1ToStrToBigDe=0.10334
d2ToStrToBigDe=1234.0
使用静态方法valueOf将double转换为BigDecimal类型并输出:
BigDecimal.valueOf(d1)=0.10334
BigDecimal.valueOf(d2)=1234.0
直接将double类型值转换为确定精度为3的BigDecimal类型值:
d1ToBigDeWithPre=0.103
d2ToBigDeWithPre=1.23E+3
将double类型值转换为String类型再转换为确定精度(3)的BigDecimal类型值:
d1ToStrToBigDeWithPre=0.103
d2ToStrToBigDeWithPre=1.23E+3