float类型和double类型精度失真
float类型和double类型在高精度场景下会出现精度失真的情况。
例如:
System.out.println(0.99999999f); // 八个9
输出:
1.0
这就涉及浮点数在计算机中的表示方式。
浮点数的表示方式:
S:符号位(0正1负)
M:尾数(小数点后面的数)
E:阶码(指数)
阶码(指数):
float类型:8位,表示范围为-127 ~ 128;
double类型:11位,表示范围为-1023 ~ 1024
尾数部分:
float类型:23位,十进制2^23=8388608,所以十进制精度只有6 ~ 7位;
double类型:52位,十进制就是 2^52 = 4503599627370496,所以十进制精度只有15 ~ 16位。
浮点数二进制表示实例:(转换工具:http://binaryconvert.com/convert_float.html)
-0.5转化为二进制数为:-0.1
即S=1, M=0, E=-1
进行二进制浮点数表示时,E需加127(即为126)(二进制:01111110)
所以-0.5f二进制浮点数表示为:
10111111 00000000 00000000 00000000
来看1.0f和0.9999999f的浮点数二进制表示:
1.0转化为二进制数为:1.0
即S=0, M=0, E=0
进行二进制浮点数表示时,E需加127(二进制:01111111)
所以1.0f二进制浮点数表示为:
00111111 10000000 00000000 00000000
同理,0.99999999f二进制浮点数表示为:
00111111 10000000 00000000 00000000
显然,在计算机中1.0f和0.999999999f的浮点数表示是一致的。
在金融领域这样的精度失真将是致命的。Java提供了BigDecimal类实现高精度计算。
BigDecimal类
BigDecimal 由任意精度的整数非标度值 和32 位的整数标度 (scale) 组成。如果为零或正数,则标度是小数点后的位数。如果为负数,则将该数的非标度值乘以 10 的负scale 次幂。因此,BigDecimal表示的数值是(unscaledValue×10-scale)。BigDecimal对象表示的是不可变的,任意精度的有符号十进制数。
构造器
例:
BigDecimal num = new BigDecimal(0.1); // 利用double型构造 System.out.println(num); // 0.1000000000000000055511151231257827021181583404541015625 BigDecimal num = new BigDecimal("0.1"); // 利用String构造 System.out.println(num); // 0.1 // 利用数值型进行构造会产生不可预见性,通常使用字符串构造。 char[] arr = {‘1‘,‘2‘,‘3‘,‘4‘,‘5‘,‘6‘}; BigDecimal num = new BigDecimal(arr,1,4); // 使用指定字符数组,指定索引及长度构造 System.out.println(num); // 2345
BigDecimal中的RoundingMode类
舍入规则
例:
BigDecimal positive_num = new BigDecimal("0.145"); BigDecimal negative_num = new BigDecimal("-0.145"); System.out.println(positive_num.setScale(2,RoundingMode.CEILING)); // 0.15 向正无穷大的方向舍入(Math.round即为此模式) System.out.println(negative_num.setScale(2,RoundingMode.CEILING)); // -0.14 向正无穷大的方向舍入 System.out.println(positive_num.setScale(2,RoundingMode.FLOOR)); // 0.14 向负无穷大的方向舍入 System.out.println(negative_num.setScale(2,RoundingMode.FLOOR)); // -0.15 向负无穷大的方向舍入 System.out.println(positive_num.setScale(2,RoundingMode.UP)); // 0.15 向远离零的方向舍入 System.out.println(negative_num.setScale(2,RoundingMode.UP)); // -0.15 向远离零的方向舍入 System.out.println(positive_num.setScale(2,RoundingMode.DOWN)); // 0.14 向接近零的方向舍入 System.out.println(negative_num.setScale(2,RoundingMode.DOWN)); // -0.14 向接近零的方向舍入 System.out.println(positive_num.setScale(2,RoundingMode.HALF_UP)); // 0.15 四舍五入模式 System.out.println(negative_num.setScale(2,RoundingMode.HALF_UP)); // -0.15 四舍五入模式 System.out.println(positive_num.setScale(2,RoundingMode.HALF_DOWN));// 0.14 五舍六入模式 System.out.println(negative_num.setScale(2,RoundingMode.HALF_DOWN));// -0.14 五舍六入模式 /** * 银行家舍入(Banker‘s Round),金融领域尽量使用此舍入模式 * 舍去位小于5,直接舍去; * 舍去位大于等于6,进位后舍去; * 舍去位等于5时: * 5后有非0数字,进位后舍去 * 5后面是0,则5的前一位是奇数进位,偶数舍去 */ BigDecimal num1 = new BigDecimal("0.254"); // 舍去位小于5 System.out.println(num1.setScale(2,RoundingMode.HALF_EVEN)); // 0.25 BigDecimal num2 = new BigDecimal("0.256"); // 舍去位大于等于6 System.out.println(num2.setScale(2,RoundingMode.HALF_EVEN)); // 0.26 BigDecimal num3 = new BigDecimal("0.2551"); // 舍去位等于5,5后面非0 System.out.println(num3.setScale(2,RoundingMode.HALF_EVEN)); // 0.26 BigDecimal num4 = new BigDecimal("0.255"); // 舍去位等于5,5后面是0,5的前一位是奇数进位 System.out.println(num4.setScale(2,RoundingMode.HALF_EVEN)); // 0.26
BigDecimal常用方法的使用
例:
BigDecimal num1 = new BigDecimal("125"); BigDecimal num2 = new BigDecimal("4"); System.out.println(num1.add(num2)); // 加法 System.out.println(num1.divide(num2)); // 除法 System.out.println(num1.divide(num2, RoundingMode.CEILING)); // 指定舍入规则的除法 System.out.println(num1.divide(num2, 1,RoundingMode.CEILING)); // 指定小数位数和舍入规则的除法 System.out.println(num1.divideToIntegralValue(num2)); // 除法只保留整数部分 System.out.println(num1.remainder(num2)); // 除法只保留余数 BigDecimal[] result = num1.divideAndRemainder(num2); // 带余数除法 for (BigDecimal bigDecimal : result) { System.out.println(bigDecimal.toString()); } System.out.println(num1.pow(2)); // 次方 System.out.println(num1.subtract(num2)); // 减法 System.out.println(num1.multiply(num2)); // 乘法 BigDecimal num3 = new BigDecimal("-2"); System.out.println(num3.abs()); // 绝对值 System.out.println(num1.compareTo(num2)); // num1>num2返回1;num1>num2返回-1;num1==num2返回0 byte i = num1.byteValueExact(); // 转化为byte类型,-128<=num1<=127,否则抛异常 System.out.println(i); System.out.println(num1.byteValue()); // 转化为byte类型,越界值出错,不抛异常 int int_value = num1.intValue(); // 转化为int类型 System.out.println(int_value); float float_value = num1.floatValue(); // 转化为float类型 System.out.println(float_value); double double_value = num1.doubleValue(); // 转化为double类型 System.out.println(double_value); long long_value = num1.longValue(); // 转化为long类型 System.out.println(long_value); System.out.println(num1.max(num2)); // 返回两者最大值 System.out.println(num1.min(num2)); // 返回两者最小值 BigDecimal num4 = new BigDecimal("12.345"); System.out.println(num4.movePointLeft(1)); // 1.2345 小数点左移1位 System.out.println(num4.movePointLeft(-1)); // 123.45 小数点右移1位 System.out.println(num4.movePointRight(1)); // 123.45 小数点右移1位 System.out.println(num4.movePointRight(-1)); // 1.2345 小数点左移1位 System.out.println(num4.negate()); // 返回"-num" System.out.println(num4.plus()); // 返回"+num" System.out.println(num4.precision()); // 5 返回精度值 System.out.println(num4.scale()); // 3 返回小数位数 System.out.println(num4.scaleByPowerOfTen(2)); // 乘以10^2 System.out.println(num4.setScale(2,RoundingMode.UP)); // 指定舍入模式设置小数点位数 System.out.println(num4.signum()); // 判断符号,正数返回1,负数返回-1 System.out.println(num4.ulp()); // 0.001 0值返回1,其他返回精确到的最小单位 System.out.println(num4.unscaledValue()); // 返回乘以10^this.scale的BigInteger,相当于去掉小数点 System.out.println(num4.stripTrailingZeros()); // 去除尾部无效的0 System.out.println(num4.toBigInteger()); // 返回BigInteger(小数部分被丢弃) System.out.println(num4.toEngineeringString()); // 返回工程计数字符串 BigDecimal num5 = new BigDecimal("0.1234"); BigDecimal num6 = num5.pow(32); System.out.println(num6.toString()); // 适当的时候会进行计数法表示的字符串 System.out.println(num6.toPlainString()); // 不修饰本色显示字符串 /** * 格式化 */ BigDecimal num7 = new BigDecimal("0.1234"); NumberFormat currency = NumberFormat.getCurrencyInstance(); // 货币格式化引用 NumberFormat percent = NumberFormat.getPercentInstance(); // 百分比格式化引用 percent.setMaximumFractionDigits(2); // 百分比小数点后最多2位 System.out.println(currency.format(num7)); // ¥0.12 System.out.println(percent.format(num7)); // 12.34%
原文:https://www.cnblogs.com/jiazhongxin/p/12837564.html