先看一下double类型精度丢失问题
(1)加法运算。
public static void main(String[] args) {
double number1 = 1;
double number2 = 20.2;
double number3 = 300.03;
double result = number1 + number2 + number3;
System.out.println("使用double运算结果: "+result);
}
打印结果如下:
使用double运算结果: 321.22999999999996。。。
为什么double类型会出现精度丢失问题?
我们知道,计算机发展了如此长的一段时间,但它始终只能识别0和1(即二进制)。无论我们使用哪种编程语言,在哪种编译环境下工作,都要先把源代码翻译成二进制的机器码才能被计算机所识别。
举个简单的例子,在源程序里面2.4,是十进制的,但计算机不能直接识别,要先编译成二进制。
那么问题来了,2.4的二进制并非是精确的2.4,反而是最为接近的二进制表示是2.39999999996。
浮点数由两部分组成:指数和尾数。如果知道怎么进行浮点数的二进制和十进制转换,应该不难理解。
如果在这个转换过程中,浮点数参与了计算,那么在转换的过程中就会变得不可预知,并且变得不可逆。我们有理由相信,就是在这个过程中,发生了精度的丢失。
而至于为什么有些浮点计算会得到准确的结果,应该也是碰巧那个计算二进制和十进制之间能够准确转换。
而当输出单个浮点型数据时,可以正确输出,如:
Double num3 = 2.4;
System.out.println(num3);
输出的结果是2.4,而不是2.39999999996。也就是说,不进行浮点计算的时候,在十进制里浮点数能正确显示。
这正印证了上面的说法,即如果浮点数参与了计算,那么浮点数二进制与十进制的转换过程就会变得不可预知,并且变得不可逆。
在Java中,浮点数不精确,因为计算机内部无法用二进制的小数来精确的表达。
经典问题:浮点数精度丢失
精度丢失的问题是在其他计算机语言中也都会出现,float和double类型的数据在执行二进制浮点运算的时候,并没有提供完全精确的结果。
产生误差不在于数的大小,而是因为数的精度。
关于浮点数存储精度丢失的问题,话题过于庞大,感兴趣的同学可以自行搜索一下:【解惑】剖析float型的内存存储和精度丢失问题
这里简单讨论一下十进制数转二进制为什么会出现精度丢失的现象,十进制数分为整数部分和小数部分,我们分开来看看就知道原因为何:
十进制整数如何转化为二进制整数?
将被除数每次都除以2,只要除到商为0就可以停止这个过程。
5 / 2 = 2 余 1
2 / 2 = 1 余 0
1 / 2 = 0 余 1
// 结果为 101
这个算法永远都不会无限循环,整数永远都可以使用二进制数精确表示,但小数呢?
十进制小数如何转化为二进制数?
每次将小数部分乘2,取出整数部分,如果小数部分为0,就可以停止这个过程
0.1 * 2 = 0.2 取整数部分0
0.2 * 2 = 0.4 取整数部分0
0.4 * 2 = 0.8 取整数部分0
0.8 * 2 = 1.6 取整数部分1
0.6 * 2 = 1.2 取整数部分1
0.2 * 2 = 0.4 取整数部分0
//... 我想写到这就不必再写了,你应该也已经发现,上面的过程已经开始循环,小数部分永远不能为0
这个算法有一定概率会存在无限循环,即无法用有限长度的二进制数表示十进制的小数,这就是精度丢失问题产生的原因。
浮点数并不适合用于精确计算,而适合进行科学计算。
float和double型用来表示带有小数点的数,那为什么我们不称他们为小数或实数,要叫浮点数呢?是因为这些数都以科学计数法的形式存储。
当一个数如50.534,转换成科学计数法的形式为5.053e1,它 的小数点移动到了一个新的位置(即浮动了)。
可见,浮点数本来就是用于科学计算的,用来进行精确计算实在太不合适了。
如何用BigDecimal解决double精度问题?
我们已经明白为什么精度会存在丢失现象,那么我们就应该知道,当某个业务场景对double数据的精度要求非常高时,就必须采取某种手段来处理这个问题,
这也是BigDecimal为什么会被广泛应用于金额支付场景中的原因啦。
BigDecimal类位于java.math包下,用于对超过16位有效位的数进行精确的运算。
一般来说,double类型的变量可以处理16位有效数,
但实际应用中,如果超过16位,就需要BigDecimal类来操作。
new BigDecimal(double val)
该方法是不可预测的,以0.1为例,你以为你传了一个double类型的0.1,最后会返回一个值为0.1的BigDecimal吗?不会的,原因在于,0.1无法用有限长度的二进制数表示,无法精确地表示为双精度数,最后的结果会是0.100000xxx。
new BigDecimal(String val)
该方法是完全可预测的,也就是说你传入一个字符串"0.1",他就会给你返回一个值完全为0,1的BigDecimal,官方也表示,能用这个构造函数就用这个构造函数叭。
BigDecimal.valueOf(double val)
第二种构造方式已经足够优秀,可你还是想传入一个double值,怎么办呢?官方其实提供给你思路并且实现了它,可以使用Double.toString(double val)先将double值转为String,再调用第二种构造方式,你可以直接使用静态方法:valueOf(double val)。
小结:将double转为BigDecimal的时候,需要先把double转换为字符串,然后再作为BigDecimal(String val)构造函数的参数,这样才能避免出现精度问题。
以下代码在项目里,项目地址:https://gitee.com/urasaki/RxJava2AndRetrofit2
以下是提供的double精确运算工具类(5位后边的四舍五入,小数位可自定义,至多可以保留6位小数,如果计算的数值总和很大,超过50 0000,请使用带有DoubleToStr的方法,不然可能会有保留小数位出现问题,不然也可能double数字会转成科学计数法显示)
package com.phone.common_library.manager;
import java.math.BigDecimal;
import java.text.NumberFormat;
/**
* double精确运算工具类(5位后边的四舍五入,小数位可自定义,至多可以保留5位小数,如果计算的数值总和很大,超过50 0000,请使用带有DoubleToStr的方法,
* 不然可能会有保留小数位出现问题,不然也可能double数字会转成科学计数法显示)
*
*
*/
public class BigDecimalManager {
/**
* double类型的加法运算(需要舍入,保留5位小数,小数位可自定义)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static double additionDouble(double m1, double m2, int scale) {
BigDecimal p1 = new BigDecimal(Double.toString(m1));
BigDecimal p2 = new BigDecimal(Double.toString(m2));
return p1.add(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* double类型的超大数值加法运算(需要舍入,保留5位小数,小数位可自定义)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static String additionDoubleToStr(double m1, double m2, int scale) {
BigDecimal p1 = new BigDecimal(Double.toString(m1));
BigDecimal p2 = new BigDecimal(Double.toString(m2));
return p1.add(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString();
}
/**
* double类型的减法运算(需要舍入,保留5位小数,小数位可自定义)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static double subtractionDouble(double m1, double m2, int scale) {
BigDecimal p1 = new BigDecimal(Double.toString(m1));
BigDecimal p2 = new BigDecimal(Double.toString(m2));
return p1.subtract(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* double类型的超大数值减法运算(需要舍入,保留5位小数,小数位可自定义)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static String subtractionDoubleToStr(double m1, double m2, int scale) {
BigDecimal p1 = new BigDecimal(Double.toString(m1));
BigDecimal p2 = new BigDecimal(Double.toString(m2));
return p1.subtract(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString();
}
/**
* double类型的乘法运算(需要舍入,保留5位小数,小数位可自定义)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static double multiplicationDouble(double m1, double m2, int scale) {
BigDecimal p1 = new BigDecimal(Double.toString(m1));
BigDecimal p2 = new BigDecimal(Double.toString(m2));
return p1.multiply(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* double类型的超大数值的乘法运算(需要舍入,保留5位小数,小数位可自定义)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static String multiplicationDoubleToStr(double m1, double m2, int scale) {
BigDecimal p1 = new BigDecimal(Double.toString(m1));
BigDecimal p2 = new BigDecimal(Double.toString(m2));
return p1.multiply(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString();
}
/**
* double类型的超大数值的乘法运算(需要舍入,保留5位小数,小数位可自定义)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static String multiplicationDoubleToStr(String m1, String m2, int scale) {
BigDecimal p1 = new BigDecimal(m1);
BigDecimal p2 = new BigDecimal(m2);
return p1.multiply(p2).setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString();
}
/**
* double类型的除法运算(需要舍入,保留5位小数,小数位可自定义)
* @param m1
* @param m2
* @param scale
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static double divisionDouble(double m1, double m2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("Parameter error");
}
BigDecimal p1 = new BigDecimal(Double.toString(m1));
BigDecimal p2 = new BigDecimal(Double.toString(m2));
return p1.divide(p2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* double类型的超大数值的除法运算(需要舍入,保留5位小数,小数位可自定义)
* @param m1
* @param m2
* @param scale
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static String divisionDoubleToStr(double m1, double m2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("Parameter error");
}
BigDecimal p1 = new BigDecimal(Double.toString(m1));
BigDecimal p2 = new BigDecimal(Double.toString(m2));
return p1.divide(p2, scale, BigDecimal.ROUND_HALF_UP).toPlainString();
}
/**
* double类型的保留小数(自定义保留5位小数,至多保留5位小数,舍去后边的值不要,小数位不足5位不用补零)
* @param m1
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static double getDoubleKeepDecimals(double m1, int scale) {
BigDecimal p1 = new BigDecimal(Double.toString(m1));
double value = p1.setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();
return value;
}
/**
* double类型的保留小数(自定义保留5位小数,至多保留5位小数,舍去后边的值不要,小数位不足5位不用补零)
* @param m1
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static String getDoubleKeepDecimalsToStr(double m1, int scale) {
BigDecimal p1 = new BigDecimal(Double.toString(m1));
String value = p1.setScale(scale, BigDecimal.ROUND_HALF_UP).toPlainString();
return value;
}
}
测试、验证小数点5位四舍五入的问题是否解决:
double m1 = 986790.278576897;
double m2 = 1887906.795768;
double numAdd = BigDecimalManager.additionDouble(m1, m2, 5);
LogManager.i(TAG, "numAdd*****" + numAdd);
double n1 = 1870689.79557790;
double n2 = 987900.27876876656;
double numSub = BigDecimalManager.subtractionDouble(n1, n2, 5);;
LogManager.i(TAG, "numSub*****" + numSub);
double o1 = 9860.2785667;
double o2 = 1000;
//这个因为超过了50 0000,已经出现问题了
double numMul = BigDecimalManager.multiplicationDouble(o1, o2, 5);
LogManager.i(TAG, "numMul*****" + numMul);
String r1 = "98679007.27798867";
String r2 = "1000";
String numMulStr = BigDecimalManager.multiplicationDoubleToStr(r1, r2, 0);
LogManager.i(TAG, "numMulStr*****" + numMulStr);
String s1 = "98679007.27798867";
String s2 = "1000.55859767";
String numMulStr2 = BigDecimalManager.multiplicationDoubleToStr(s1, s2, 5);
LogManager.i(TAG, "numMulStr2*****" + numMulStr2);
double p1 = 9867900.278676575;
double p2 = 18790689.795565;
double numDiv = BigDecimalManager.divisionDouble(p1, p2, 5);
LogManager.i(TAG, "numDiv*****" + numDiv);
double q1 = 9867900.278590876;
double numKeepDecimals = BigDecimalManager.getDoubleKeepDecimals(q1, 5);
LogManager.i(TAG, "numKeepDecimals*****" + numKeepDecimals);
double z1 = 98679689078000.278590876596890;
String numKeepDecimalsStr = BigDecimalManager.getDoubleKeepDecimalsToStr(z1, 5);
LogManager.i(TAG, "numKeepDecimalsStr*****" + numKeepDecimalsStr);
打印结果如下:
2022-08-06 15:27:46.365 5777-5777/com.phone.rxjava2andretrofit2 I/DecimalOperationActivity: numAdd*****2874697.07434
2022-08-06 15:27:46.365 5777-5777/com.phone.rxjava2andretrofit2 I/DecimalOperationActivity: numSub*****882789.51681
2022-08-06 15:27:46.365 5777-5777/com.phone.rxjava2andretrofit2 I/DecimalOperationActivity: numMul*****9860278.5667
2022-08-06 15:27:46.365 5777-5777/com.phone.rxjava2andretrofit2 I/DecimalOperationActivity: numMulStr*****98679007278
2022-08-06 15:27:46.366 5777-5777/com.phone.rxjava2andretrofit2 I/DecimalOperationActivity: numMulStr2*****98734129141.53207
2022-08-06 15:27:46.366 5777-5777/com.phone.rxjava2andretrofit2 I/DecimalOperationActivity: numDiv*****0.52515
2022-08-06 15:27:46.366 5777-5777/com.phone.rxjava2andretrofit2 I/DecimalOperationActivity: numKeepDecimals*****9867900.27859
2022-08-06 15:27:46.366 5777-5777/com.phone.rxjava2andretrofit2 I/DecimalOperationActivity: numKeepDecimalsStr*****98679689078000.28000
新需求(知识拓展)
现在有一个需求是直接舍去小数点5位后边的数不要,就稍微有一点点繁琐了,文中带DoubleCompatible字段的方法都是直接舍去小数点5位(可自定义)后边的数不要,至多可以保留5位小数(可自定义)。
注意:如果计算的数值总和很大,*超过50 0000,请使用带有DoubleCompatibleToStr的方法,不然可能会有保留小数位出现问题,不然也可能double数字会转成科学计数法显示,*但是带有DoubleCompatibleToStr的方法计算之后的总和不要超过900 0000 0000,不然BigDecimal也无法提供舍去小数点5位后边的数不要的精确计算了(只能用上边的四舍五入进行计算了)。
package com.phone.common_library.manager;
import java.math.BigDecimal;
import java.text.NumberFormat;
/**
* double精确运算工具类,直接舍去小数点5位后边的数不要,就稍微有一点点繁琐了,文中带DoubleCompatible字段的方法都是直接舍去小数点5位后边的数不要,至多可以保留5位小数。
* 注意:如果计算的数值总和很大,超过50 0000,请使用带有DoubleCompatibleToStr的方法,不然可能会有保留小数位出现问题,不然也可能double数字会转成科学计数法显示,
* 但是带有DoubleCompatibleToStr的方法计算之后的总和不要超过900 0000 0000,不然BigDecimal也无法提供舍去小数点5位后边的数不要的精确计算了(只能用上边的四舍五入进行计算了)
*
*/
public class BigDecimalManager {
/**
* double类型的加法运算兼容(自定义保留5位小数,至多保留5位小数,舍去后边的值不要,小数位不足5位不用补零)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static double additionDoubleCompatible(double m1, double m2, int scale) {
BigDecimal p1 = new BigDecimal(Double.toString(m1));
BigDecimal p2 = new BigDecimal(Double.toString(m2));
double value = p1.add(p2).setScale(scale, BigDecimal.ROUND_DOWN).doubleValue();
return keepDecimalsCompatibleToDou(value, scale);
}
/**
* double类型的加法运算兼容(自定义保留5位小数,至多保留5位小数,舍去后边的值不要,小数位不足5位不用补零)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static double additionDoubleCompatible(String m1, String m2, int scale) {
BigDecimal p1 = new BigDecimal(m1);
BigDecimal p2 = new BigDecimal(m2);
double value = p1.add(p2).setScale(scale, BigDecimal.ROUND_DOWN).doubleValue();
return keepDecimalsCompatibleToDou(value, scale);
}
/**
* double类型的加法运算兼容,可以把double类型的科学计数法转化成小数字符串,
* 主要用来显示小数(自定义保留5位小数,至多保留5位小数,舍去后边的值不要,小数位不足5位不用补零)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static String additionDoubleCompatibleToStr(String m1, String m2, int scale) {
BigDecimal p1 = new BigDecimal(m1);
BigDecimal p2 = new BigDecimal(m2);
return p1.add(p2).setScale(scale, BigDecimal.ROUND_DOWN).toPlainString();
}
/**
* double类型的减法运算兼容(自定义保留5位小数,至多保留5位小数,舍去后边的值不要,小数位不足5位不用补零)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static double subtractionDoubleCompatible(double m1, double m2, int scale) {
BigDecimal p1 = new BigDecimal(Double.toString(m1));
BigDecimal p2 = new BigDecimal(Double.toString(m2));
double value = p1.subtract(p2).setScale(scale, BigDecimal.ROUND_DOWN).doubleValue();
return keepDecimalsCompatibleToDou(value, scale);
}
/**
* double类型的减法运算兼容(自定义保留5位小数,至多保留5位小数,舍去后边的值不要,小数位不足5位不用补零)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static double subtractionDoubleCompatible(String m1, String m2, int scale) {
BigDecimal p1 = new BigDecimal(m1);
BigDecimal p2 = new BigDecimal(m2);
double value = p1.subtract(p2).setScale(scale, BigDecimal.ROUND_DOWN).doubleValue();
return keepDecimalsCompatibleToDou(value, scale);
}
/**
* double类型的加法运算兼容,可以把double类型的科学计数法转化成小数字符串,
* 主要用来显示小数(自定义保留5位小数,至多保留5位小数,舍去后边的值不要,小数位不足5位不用补零)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static String subtractionDoubleCompatibleToStr(String m1, String m2, int scale) {
BigDecimal p1 = new BigDecimal(m1);
BigDecimal p2 = new BigDecimal(m2);
return p1.subtract(p2).setScale(scale, BigDecimal.ROUND_DOWN).toPlainString();
}
/**
* double类型的除法运算兼容(自定义保留5位小数,至多保留5位小数,舍去后边的值不要,小数位不足5位不用补零)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static double multiplicationDoubleCompatible(double m1, double m2, int scale) {
BigDecimal p1 = new BigDecimal(Double.toString(m1));
BigDecimal p2 = new BigDecimal(Double.toString(m2));
double value = p1.multiply(p2).setScale(scale, BigDecimal.ROUND_DOWN).doubleValue();
return keepDecimalsCompatibleToDou(value, scale);
}
/**
* double类型的加法运算兼容,可以把double类型的科学计数法转化成小数字符串,
* 主要用来显示小数(自定义保留5位小数,至多保留5位小数,舍去后边的值不要,小数位不足5位不用补零)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static String multiplicationDoubleCompatibleToStr(double m1, double m2, int scale) {
BigDecimal p1 = new BigDecimal(Double.toString(m1));
BigDecimal p2 = new BigDecimal(Double.toString(m2));
return p1.multiply(p2).setScale(scale, BigDecimal.ROUND_DOWN).toPlainString();
}
/**
* double类型的加法运算兼容,可以把double类型的科学计数法转化成小数字符串,
* 主要用来显示小数(自定义保留5位小数,至多保留5位小数,舍去后边的值不要,小数位不足5位不用补零)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static String multiplicationDoubleCompatibleToStr(String m1, String m2, int scale) {
BigDecimal p1 = new BigDecimal(m1);
BigDecimal p2 = new BigDecimal(m2);
return p1.multiply(p2).setScale(scale, BigDecimal.ROUND_DOWN).toPlainString();
}
/**
* double类型的除法运算兼容(自定义保留5位小数,至多保留5位小数,舍去后边的值不要,小数位不足5位不用补零)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static double divisionDoubleCompatible(double m1, double m2, int scale) {
BigDecimal p1 = new BigDecimal(Double.toString(m1));
BigDecimal p2 = new BigDecimal(Double.toString(m2));
double value = p1.divide(p2, scale, BigDecimal.ROUND_DOWN).doubleValue();
return keepDecimalsCompatibleToDou(value, scale);
}
/**
* double类型的加法运算兼容,可以把double类型的科学计数法转化成小数字符串,
* 主要用来显示小数(自定义保留5位小数,至多保留5位小数,舍去后边的值不要,小数位不足5位不用补零)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static String divisionDoubleCompatibleToStr(double m1, double m2, int scale) {
BigDecimal p1 = new BigDecimal(Double.toString(m1));
BigDecimal p2 = new BigDecimal(Double.toString(m2));
return p1.divide(p2, scale, BigDecimal.ROUND_DOWN).toPlainString();
}
/**
* double类型的除法运算兼容(自定义保留5位小数,至多保留5位小数,舍去后边的值不要,小数位不足5位不用补零)
* @param m1
* @param m2
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static double divisionDoubleCompatible(String m1, String m2, int scale) {
BigDecimal p1 = new BigDecimal(m1);
BigDecimal p2 = new BigDecimal(m2);
double value = p1.divide(p2, scale, BigDecimal.ROUND_DOWN).doubleValue();
return keepDecimalsCompatibleToDou(value, scale);
}
/**
* double类型的保留小数(自定义保留5位小数,至多保留5位小数,舍去后边的值不要,小数位不足5位不用补零)
* @param m1
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static double getDoubleKeepDecimalsCompatible(double m1, int scale) {
BigDecimal p1 = new BigDecimal(Double.toString(m1));
double value = p1.setScale(scale, BigDecimal.ROUND_DOWN).doubleValue();
return keepDecimalsCompatibleToDou(value, scale);
}
/**
* double类型的加法运算兼容,可以把double类型的科学计数法转化成小数字符串,
* 主要用来显示小数(自定义保留5位小数,至多保留5位小数,舍去后边的值不要,小数位不足5位不用补零)
* @param m1
* @return 不加doubleValue()则, 返回BigDecimal对象
*/
public static String getDoubleKeepDecimalsCompatibleToStr(double m1, int scale) {
BigDecimal p1 = new BigDecimal(Double.toString(m1));
return p1.setScale(scale, BigDecimal.ROUND_DOWN).toPlainString();
}
private static double keepDecimalsCompatibleToDou(double value, int scale) {
//将科学计数法转化成小数
NumberFormat numberFormat = NumberFormat.getInstance();
numberFormat.setGroupingUsed(false);
// 设置数的小数部分所允许的最小位数
numberFormat.setMinimumFractionDigits(0);
// 设置数的小数部分所允许的最大位数
numberFormat.setMaximumFractionDigits(scale);
String valueStr = numberFormat.format(value);
//返回的double数据如果很大还是科学计数法的,如果需要显示,建议返回String类型的
if (valueStr.contains(".")){
String[] valueStrArr = valueStr.split("\\.");
if (valueStrArr.length > 1 && valueStrArr[1].length() > 0){
if (scale <= 0){
return Double.parseDouble(valueStrArr[0]);
} else {
if (valueStrArr[1].length() < scale){
return Double.parseDouble(valueStrArr[0] + "." + valueStrArr[1]);
} else {
return Double.parseDouble(valueStrArr[0] + "." + valueStrArr[1].substring(0, scale));
}
}
} else {
return Double.parseDouble(valueStr);
}
} else {
return Double.parseDouble(valueStr);
}
}
private static String keepDecimalsCompatibleToStr(double value, int scale) {
//将科学计数法转化成小数
NumberFormat numberFormat = NumberFormat.getInstance();
numberFormat.setGroupingUsed(false);
// 设置数的小数部分所允许的最小位数
numberFormat.setMinimumFractionDigits(0);
// 设置数的小数部分所允许的最大位数
numberFormat.setMaximumFractionDigits(scale);
String valueStr = numberFormat.format(value);
if (valueStr.contains(".")){
String[] valueStrArr = valueStr.split("\\.");
if (valueStrArr.length > 1 && valueStrArr[1].length() > 0){
if (scale <= 0){
return valueStrArr[0];
} else {
if (valueStrArr[1].length() < scale){
return valueStrArr[0] + "." + valueStrArr[1];
} else {
return valueStrArr[0] + "." + valueStrArr[1].substring(0, scale);
}
}
} else {
return valueStr;
}
} else {
return valueStr;
}
}
}
测试、验证舍去小数点3位后边的数不要的问题是否解决:
double mCompatible1 = 986790.27857;
double mCompatible2 = 1887906.79501;
//这个因为超过了50 0000,可能会出现问题
double numAddCompatible = BigDecimalManager.additionDoubleCompatible(mCompatible1, mCompatible2, 5);
LogManager.i(TAG, "numAddCompatible*****" + numAddCompatible);
double nCompatible1 = 1870689.795572;
double nCompatible2 = 987900.278768;
//这个因为超过了50 0000,已经出现问题了
double numSubCompatible = BigDecimalManager.subtractionDoubleCompatible(nCompatible1, nCompatible2, 5);;
LogManager.i(TAG, "numSubCompatible*****" + numSubCompatible);
double oCompatible1 = 9860.2785667;
double oCompatible2 = 1000;
//这个因为超过了50 0000,可能会出现问题
double numMulCompatible = BigDecimalManager.multiplicationDoubleCompatible(oCompatible1, oCompatible2, 5);
LogManager.i(TAG, "numMulCompatible*****" + numMulCompatible);
String rCompatible1 = "98679007.27798867";
String rCompatible2 = "1000";
String numMulCompatibleStr = BigDecimalManager.multiplicationDoubleCompatibleToStr(rCompatible1, rCompatible2, 0);
LogManager.i(TAG, "numMulCompatibleStr*****" + numMulCompatibleStr);
String sCompatible1 = "98679007.27798";
String sCompatible2 = "1000.55859";
//这个因为超过了900 0000 0000,可能会出现问题
String numMulCompatibleStr2 = BigDecimalManager.multiplicationDoubleCompatibleToStr(sCompatible1, sCompatible2, 5);
LogManager.i(TAG, "numMulCompatibleStr2*****" + numMulCompatibleStr2);
double pCompatible1 = 9867900.278676575;
double pCompatible2 = 18790689.795565;
double numDivCompatible = BigDecimalManager.divisionDoubleCompatible(pCompatible1, pCompatible2, 5);
LogManager.i(TAG, "numDivCompatible*****" + numDivCompatible);
double qCompatible1 = 9867900.278590876;
//这个因为超过了50 0000,可能会出现问题
double numKeepDecimalsCompatible = BigDecimalManager.getDoubleKeepDecimalsCompatible(qCompatible1, 5);
LogManager.i(TAG, "numKeepDecimalsCompatible*****" + numKeepDecimalsCompatible);
double z1 = 98679689078000.278590876596890;
//这个因为超过了900 0000 0000,已经出现问题了
String numKeepDecimalsCompatibleStr = BigDecimalManager.getDoubleKeepDecimalsCompatibleToStr(z1, 5);
LogManager.i(TAG, "numKeepDecimalsCompatibleStr*****" + numKeepDecimalsCompatibleStr);
打印结果如下:
2022-08-11 21:51:06.522 5865-5865/com.phone.rxjava2andretrofit2 I/DecimalOperationActivity: numAddCompatible*****2874697.07358
2022-08-11 21:51:06.523 5865-5865/com.phone.rxjava2andretrofit2 I/DecimalOperationActivity: numSubCompatible*****882789.5168
2022-08-11 21:51:06.524 5865-5865/com.phone.rxjava2andretrofit2 I/DecimalOperationActivity: numMulCompatible*****9860278.5667
2022-08-11 21:51:06.525 5865-5865/com.phone.rxjava2andretrofit2 I/DecimalOperationActivity: numMulCompatibleStr*****98679007277
2022-08-11 21:51:06.526 5865-5865/com.phone.rxjava2andretrofit2 I/DecimalOperationActivity: numMulCompatibleStr2*****98734128384.6554
2022-08-11 21:51:06.527 5865-5865/com.phone.rxjava2andretrofit2 I/DecimalOperationActivity: numDivCompatible*****0.52514
2022-08-11 21:51:06.528 5865-5865/com.phone.rxjava2andretrofit2 I/DecimalOperationActivity: numKeepDecimalsCompatible*****9867900.27859
2022-08-11 21:51:06.530 5865-5865/com.phone.rxjava2andretrofit2 I/DecimalOperationActivity: numKeepDecimalsCompatibleStr*****98679689078000.28
如对此有疑问,请联系qq1164688204。
推荐Android开源项目
项目功能介绍:原本是RxJava2 和Retrofit2 项目,现已更新使用Kotlin+RxJava2+Retrofit2+MVP架构+组件化和
Kotlin+Retrofit2+协程+MVVM架构+组件化, 添加自动管理token 功能,添加RxJava2 生命周期管理,集成极光推送、阿里云Oss对象存储和高德地图定位功能。
项目地址:https://gitee.com/urasaki/RxJava2AndRetrofit2