首先论浮点数
单精度浮点数(float)在计算机存储器中占用4个字节(32 bits),利用“浮点”(浮动小数点)的方法,可以表示一个范围很大的数值。
比起单精度浮点数,双精度浮点数(double)使用 64 位(8字节) 来存储一个浮点数。
由于计算机中保存的小数其实是十进制的小数的近似值,并不是准确值,在实际应用中,需要对更大或者更小的数进行运算和处理。所以,千万不要在代码中使用浮点数来表示金额等重要的指标。
建议使用BigDecimal或者Long(单位为分)来表示金额。
float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal【强烈推荐】。
-java.math.BigDecimal
BigDecimal是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。
BigDecimal的构造方法
BigDecimal(int) 创建一个具有参数所指定整数值的对象。
BigDecimal(double) 创建一个具有参数所指定双精度值的对象。(不建议采用)
BigDecimal(long) 创建一个具有参数所指定长整数值的对象。
BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象。
关于为什么不建议采用BigDecimal(double)构造方法
1.让我们看看四个构造分别打印的内容
打印结果
JDK的描述:
1、参数类型为double的构造方法的结果有一定的不可预知性。有人可能认为在Java中写入newBigDecimal(0.1)所创建的BigDecimal正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于0.1000000000000000055511151231257827021181583404541015625。这是因为0.1无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。2、另一方面,String 构造方法是完全可预知的:写入 newBigDecimal(“0.1”) 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言,通常建议优先使用String构造方法。
当必须使用double类型时
当必须使用double类型时,请使用 String.valueOf(double) 或 Double.toString(double);
BigDecimal的数学运算
对于常用的加,减,乘,除,BigDecimal类提供了相应的成员方法。
public BigDecimal add(BigDecimal value); //加法
public BigDecimal subtract(BigDecimal value); //减法
public BigDecimal multiply(BigDecimal value); //乘法
public BigDecimal divide(BigDecimal value); //除法
示例:
运行结果
除法运算divide方法
BigDecimal除法可能出现不能整除的情况,比如 6.0/2.1,这时会报错
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
例如:
其实divide方法有可以传三个参数:
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
看下源码
/**
* Returns a new {@code BigDecimal} whose value is {@code this / divisor}.
* As scale of the result the parameter {@code scale} is used. If rounding
* is required to meet the specified scale, then the specified rounding mode
* {@code roundingMode} is applied.
*
* @param divisor
* value by which {@code this} is divided.
* @param scale
* the scale of the result returned.
* @param roundingMode
* rounding mode to be used to round the result.
* @return {@code this / divisor} rounded according to the given rounding
* mode.
* @throws NullPointerException
* if {@code divisor == null}.
* @throws IllegalArgumentException
* if {@code roundingMode} is not a valid rounding mode.
* @throws ArithmeticException
* if {@code divisor == 0}.
* @throws ArithmeticException
* if {@code roundingMode == ROUND_UNNECESSARY} and rounding is
* necessary according to the given scale.
*/
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) {
return divide(divisor, scale, RoundingMode.valueOf(roundingMode));
}
第一参数表示除数, 第二个参数表示小数点后保留位数,第三个参数表示舍入模式,只有在作除法运算或四舍五入时才用到舍入模式。
divide的舍入模式
有下面这几种:
是BigDecimal类下的常量
ROUND_CEILING // 向正无穷方向舍入 ROUND_DOWN // 向零方向舍入 ROUND_HALF_DOWN //
向(距离)最近的一边舍入,除非两边(的距离)是相等的,如果是这样,向下舍入(例如1.55保留一位小数为1.5)
ROUND_HALF_EVEN //
向(距离)最近的一边舍入,除非两边(的距离)是相等的,如果是这样,如果保留位数是奇数,使用ROUND_HALF_UP,如果是偶数,使用ROUND_HALF_DOWN
ROUND_HALF_UP //
向(距离)最近的一边舍入,除非两边(的距离)是相等的,如果是这样,向上舍入(例如1.55保留一位小数为1.6)
ROUND_UNNECESSARY // 计算结果是精确的,不需要舍入模式 ROUND_UP // 向远离0的方向舍入