当前位置: 首页 > news >正文

建设邮费自己的网站_要不要购买服务器的网站建设作品图片

建设邮费自己的网站_要不要购买服务器的,网站建设作品图片,网站开发 思维导图,常平哪里有招计算机网站开发的BigDecimal BigDecimal可以表示任意大小#xff0c;任意精度的有符号十进制数。所以不用怕精度问题#xff0c;也不用怕大小问题#xff0c;放心使用就行了。就是要注意的是#xff0c;使用的时候有一些注意点。还有就是要注意避免创建的时候存在精度问题#xff0c;尤其…BigDecimal BigDecimal可以表示任意大小任意精度的有符号十进制数。所以不用怕精度问题也不用怕大小问题放心使用就行了。就是要注意的是使用的时候有一些注意点。还有就是要注意避免创建的时候存在精度问题尤其是不要使用new BigDecimal(double val)这个构造方法来创建BigDecimal对象因为double本来就不精确。 使用场景 BigDecimal相信对于很多人来说都不陌生很多人都知道他的用法这是一种java.math包中提供的一种可以用来进行精确运算的类型。 很多人都知道在进行金额表示、金额计算等场景不能使用double、float等类型而是要使用对精度支持的更好的BigDecimal。 所以很多支付、电商、金融等业务中BigDecimal的使用非常频繁。 注意点 使用BigDecimal的equals方法并不能验证两个数是否真的相等BigDecimal的使用的第一步就是创建一个BigDecimal对象如果创建对象的时候没有正确创建那么后面怎么算都是错的 如何正确的创建一个BigDecimal 关于这个问题在《阿里巴巴Java开发手册》中有一条建议或者说是要求 这是一条【强制】建议那么这背后的原理是什么呢 想要搞清楚这个问题主要需要弄清楚以下几个问题 1、为什么说double不精确 2、BigDecimal是如何保证精确的 在知道这两个问题的答案之后我们也就大概知道为什么不能使用BigDecimal(double)来创建一个BigDecimal了。 double、float为什么不精确 首先计算机是只认识二进制的即0和1这个大家一定都知道。 那么所有数字包括整数和小数想要在计算机中存储和展示都需要转成二进制。 十进制整数转成二进制很简单通常采用除2取余逆序排列即可如10的二进制为1010。 但是小数的二进制如何表示呢 十进制小数转成二进制一般采用乘2取整顺序排列方法如0.625转成二进制的表示为0.101。 但是并不是所有小数都能转成二进制如0.1就不能直接用二进制表示他的二进制是0.000110011001100… 这是一个无限循环小数。 所以计算机是没办法用二进制精确的表示0.1的。也就是说在计算机中很多小数没办法精确的使用二进制表示出来。 那么这个问题总要解决吧。那么人们想出了一种采用一定精度的近似值表示一个小数的办法。这就是IEEE 754IEEE二进制浮点数算术标准规范的主要思想。 IEEE 754规定了多种表示浮点数的表示方式其中最常用的就是32位单精度浮点数和64位双精度浮点数。 在Java中就用了这个思想。java中使用float和double分别用来表示单精度浮点数和双精度浮点数但是注意哈java中的小数实际上还是用乘2取整顺序排列组成的0.000110011001100… 这种01序列来保存的哈。只是他是这样的比如你有一个double类型的0.1那么java允许你用0.1000000000000000055511151231257827021181583404541015625这样的一个能被01表示的、又近似等于0.1的数代替0.1在计算机中进行存储因为0.1用01序列保存是无限位的但是这个0.1000000000000000055511151231257827021181583404541015625就可以被有限个01序列表示而且保证如果只看前17位那么这两个值是一样大的这就是double的精度保证至少是17位是准确的效果。 float和double的精度不同可以简单的理解为保留有效位数不同。 float的精度是保证至少8位有效数字是准确的。double的精度是保证至少17位有效数字是准确的。 float和double的精度如下 序号数据类型描述有效位1float单精度8位2double双精度17位 其实java保存一个小数是采用保留有效位数的一个近似小数作为这个小数实际存储的值的。 比如 double d0.1; 其实d的值被计算机保存的是0.1000000000000000055511151231257827021181583404541015625。他只保证前17位的数字是正确的剩余的他就不保证了。 测试1 注意执行toString只会打印有效位数的数据。比如你打印double的数据就一定只打印前17位数字不记小数点就看纯数字你打印float的数据一定打印前8位数字。而且toString你看到的结果最后一位是被进位的。 所以大家也就知道为什么double和float表示的小数不精确了。 BigDecimal如何精确计数 BigDecimal的解决方案就是不使用二进制而是使用十进制BigInteger小数点位置(scale)来表示小数。 比如下面这个代码 public static void main(String[] args) {BigDecimal bd new BigDecimal(100.001);System.out.println(bd.scale());System.out.println(bd.unscaledValue()); }输出 也就是100.001 100001 * 0.1^3。这种表示方式下避免了小数的出现当然也就不会有精度问题了。十进制也就是整数部分使用了BigInteger来表示小数点位置只需要一个整数scale来表示就OK了。这个100001 就是无标度值3是标度值。 如果大家看过BigDecimal的源码其实可以发现实际上一个BigDecimal就是通过一个无标度值和一个标度来表示一个数的。 BigDecimal类中的属性主要包括下面五个 在BigDecimal中标度是通过scale字段来表示的。 而无标度值的表示比较复杂。 我们看三个场景 情景一 package com.liudashuai;import java.math.BigDecimal;public class Test {public static void main(String[] args) {BigDecimal b1 new BigDecimal(3.1415926);} }这个stringCache是一个String的类型intCompact是long类型的precision是int类型的scale也是int类型的 从Debug的结果看intVal为空因为当无标度值没有超过Long.MAX_VALUE无标度值会被压缩存储到intCompact中precision表示有8个数字位不算小数点scale表示标度为7表示无标度值把小数点向左移动7位就是实际值。 即无标度值没有超过Long.MAX_VALUE即9223372036854775807intVal为空并且会用intCompact存储无标度值相当于是压缩了空间intCompact用的空间更少。 情景二 package com.liudashuai;import java.math.BigDecimal;public class Test {public static void main(String[] args) {BigDecimal b2 new BigDecimal(31415926314159263141592631415926);} }当无标度值超过Long.MAX_VALUE即9223372036854775807才会用intVal记录无标度值而intCompact里面就只存一个Long.MIN_VALUE就行了。上面的precision表示当前数字位为32个scale为0表示没有小数位。 情景三 package com.liudashuai;import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode;public class Test {public static void main(String[] args) {MathContext mc3 new MathContext(30,RoundingMode.HALF_UP);BigDecimal b2 new BigDecimal(31415926314159263141592631415926,mc3);} }在这里我们手动设置了precision为30所以最后两位被丢弃并执行了舍入操作同时scale记录为-2这个-2表示无标度值可以看到上面无标度的值intVal没有看最后的26表示的数的小数点向右移动两位是实际值。 RoundingMode.HALF_UP表示四舍五入。不设置的话默认五舍六入。 你看不知定RoundingMode.HALF_UP只指定precision为30默认如果第31个数是6那么是进位的。 总结 通过上面三个例子我们对BigDecimal的5个基本属性总结如下 BigDecimal是通过unscaled value和scale来构造同时使用Long.MAX_VALUE作为我们是否压缩的阈值。当unscaled value超过阈值时采用intVal字段存储unscaled valueintCompact字段存储Long.MIN_VALUE。如果unscaled value没有超过阈值对unscaled value进行压缩存储到long型的intCompact字段并用于后续计算而intVal字段则为空。scale字段存储标度可以理解为unscaled value最后一位到实际值小数点的距离。如例1中对于3.1415926来说unscaled value为31415926最后一位6到实际值的小数点距离为7scale记为7对于例3中手动设置precision的情况unscaled value为31415926xxx159的最后一位9到实际值31415926xxx15900的小数点距离为2由于在小数点左边scale则记为-2。precision字段记录的是unscaled value的数字个数当手动指定MathContext并且指定的precision小于实际precision的时候会要求进行rounding操作。 关于无标度值的压缩机制大家了解即可不是本文的重点大家只需要知道BigDecimal主要是通过一个无标度值和标度来表示的就行了。 BigDecimal的构造方法 我们都知道想要创建一个对象需要使用该类的构造方法在BigDecimal中有很多个构造方法。 一般记下面这四个就行了 public class BigDecimal extends Number implements ComparableBigDecimal {public BigDecimal(int val) {this.intCompact val;this.scale 0;this.intVal null;}public BigDecimal(double val) {this(val,MathContext.UNLIMITED);}public BigDecimal(String val) {this(val.toCharArray(), 0, val.length());}public BigDecimal(long val) {this.intCompact val;this.intVal (val INFLATED) ? INFLATED_BIGINT : null;this.scale 0;}…… }以上四个方法创建出来的的BigDecimal的标度scale是不同的。 其中 BigDecimal(int)和BigDecimal(long) 比较简单因为都是整型所以他们的标度都是0。 而BigDecimal(double) 和BigDecimal(String)的标度就有很多学问了。 BigDecimal中提供了一个通过double创建BigDecimal的方法——BigDecimal(double) 但是同时也给我们留了一个坑 因为我们知道double表示的小数是不精确的如0.1这个数字double只能表示他的近似值。 所以当我们使用new BigDecimal(0.1)创建一个BigDecimal 的时候其实创建出来的值并不是正好等于0.1的。 而是0.1000000000000000055511151231257827021181583404541015625。这是因为doule自身表示的只是一个近似值。 所以如果我们在代码中使用BigDecimal(double) 来创建一个BigDecimal的话那么是损失了精度的这是极其严重的。 那么该如何创建一个精确的BigDecimal来表示小数呢答案是使用String创建。 而对于BigDecimal(String) 当我们使用new BigDecimal(“0.1”)创建一个BigDecimal 的时候其实创建出来的值正好就是等于0.1的。他的标度是1。是准确地表示0.1这个数的。 BigDecimal的比较 需要注意的是new BigDecimal(“0.10000”)和new BigDecimal(“0.1”)这两个数的标度分别是5和1所以如果你使用BigDecimal的equals方法进行比较的话得到的结果会是false。 例子1 例子2 package com.liudashuai;import java.math.BigDecimal;public class Test {public static void main(String[] args) {BigDecimal bigDecimal new BigDecimal(1);BigDecimal bigDecimal1 new BigDecimal(1);System.out.println(bigDecimal.equals(bigDecimal1));BigDecimal bigDecimal2 new BigDecimal(1);BigDecimal bigDecimal3 new BigDecimal(1.0);System.out.println(bigDecimal2.equals(bigDecimal3));BigDecimal bigDecimal4 new BigDecimal(1);BigDecimal bigDecimal5 new BigDecimal(1.0);System.out.println(bigDecimal4.equals(bigDecimal5));} }通过以上代码示例我们发现在使用BigDecimal的equals方法对1和1.0进行比较的时候有的时候是 true当使用 int、double 定义 BigDecimal 时有的时候是 false当使用 String 定义 BigDecimal时。 那么为什么会出现这样的情况呢我们先来看下BigDecimal的equals方法。 BigDecimal对应的代码如下 看到它比较的时候其实还比较了精度。还会比较一些其他的东西。所以值是一样的使用equals方法得到的结果可能会判断他们两个不是相等的。 上面看到bigDecimal2和bigDecimal3相等、bigDecimal和bigDecimal1相等是因为他们的精度也是一样的。 但是可能精度一样值也一样使用equals方法判断可能也不相等。我们看到源码里面还有其他一些判断我没看懂防止使用equals方法判断两个BigDecimal对象是否相等的变数比较多结果不好预测。所以不建议使用equals方法比较两个BigDecimal对象是否相等如果要比较的话建议使用compareTo方法。 如下 使用compareTo package com.liudashuai;import java.math.BigDecimal;public class Test {public static void main(String[] args) {BigDecimal bigDecimalnew BigDecimal(1.2);BigDecimal bigDecimal2new BigDecimal(1.20);int rs bigDecimal.compareTo(bigDecimal2);System.out.println(rs);BigDecimal bigDecimal3new BigDecimal(1.21);BigDecimal bigDecimal4new BigDecimal(1.20);int rs1 bigDecimal3.compareTo(bigDecimal4);System.out.println(rs1);BigDecimal bigDecimal5new BigDecimal(0.9);BigDecimal bigDecimal6new BigDecimal(1.20);int rs2 bigDecimal5.compareTo(bigDecimal6);System.out.println(rs2);/*rs -1,表示bigdemical小于bigdemical2rs 0,表示bigdemical等于bigdemical2rs 1,表示bigdemical大于bigdemical2*/} }BigDecimal的valueOf方法 package com.liudashuai;import java.math.BigDecimal;public class Test {public static void main(String[] args) {BigDecimal bigDecimal BigDecimal.valueOf(12345679.1);//结果为12345679.1System.out.println(bigDecimal);} }这个是能精准表示12345679.1的因为他的底层如下 相当于是把小数变为了字符串然后去创建BigDecimal对象所以是精准的。 但是注意 上面的情况只表示了小数点后14位的数所以实际这个bigDecimal变量里面保存的数值为123.12345678901235。因为Double.toString(123.12345678901235)返回的字符串只保留17位数。所以只能精准保存了123.12345678901235这个值。 如果要表示位数很多的一个小数建议还是使用new BigDecimal(String)来创建对象 BigDecimal常用方法 注意BigDecimal进行运算时必须要保证对象本身不能是null否则就会抛空指针异常。 方法含义add(BigDecimal)BigDecimal对象中的值相加返回BigDecimal对象subtract(BigDecimal)BigDecimal对象中的值相减返回BigDecimal对象multiply(BigDecimal)BigDecimal对象中的值相乘返回BigDecimal对象divide(BigDecimal)BigDecimal对象中的值相除返回BigDecimal对象。该方法可能会遇到无限精度问题会抛出异常使用时需注意。详细见下方的无限精度的坑abs()将BigDecimal对象中的值转换成绝对值doubleValue()将BigDecimal对象中的值转换成双精度数floatValue()将BigDecimal对象中的值转换成单精度数longValue()将BigDecimal对象中的值转换成长整数intValue()将BigDecimal对象中的值转换成整数compareTo(BigDecimal val)比较大小返回int类型。0相等 1大于 -1小于toString()有必要时使用科学计数法。toPlainString()不使用任何指数。推荐使用toEngineeringString()有必要时使用工程计数法。 工程记数法是一种工程计算中经常使用的记录数字的方法与科学技术法类似但要求10的幂必须是3的倍数max(BigDecimal val)两值比较返回最大值negate()求相反数正变负负变正pow(int n)求乘方如BigDecimal.valueOf(2).pow(3)的值为8 代码示例: package com.liudashuai;import java.math.BigDecimal;public class Test {public static void main(String[] args) {BigDecimal b1 new BigDecimal(1.1);BigDecimal b2 new BigDecimal(2.5);BigDecimal b3 new BigDecimal(4);BigDecimal b4 new BigDecimal(-5.53);System.out.println(相加b1.add(b2));System.out.println(相减b1.subtract(b2));System.out.println(相乘b2.multiply(b3));System.out.println(相除b2.divide(b3));System.out.println(绝对值b4.abs());//有时候我们进行精确运算后还需要转为对应的类型我们可以用下面这些APIdouble v b1.doubleValue();float f b1.floatValue();long l b1.longValue();int i b1.intValue();System.out.println(v);System.out.println(f);System.out.println(l);System.out.println(i);System.out.println(new BigDecimal(1.9).intValue());//输出1可以看出把小数的BigDecimal转为整数会直接丢掉小数只看整数部分。System.out.println(1.1和-5.53中较大的值为b4.max(b1));System.out.println(-5.53的相反数是b4.negate());System.out.println(4的三次方是b3.pow(3));} }上面三个返回值字符串的方法如下 toPlainString() : 不使用任何指数。toString() 有必要时使用科学计数法。toEngineeringString()有必要时使用工程计数法。 工程记数法是一种工程计算中经常使用的记录数字的方法与科学技术法类似但要求10的幂必须是3的倍数 打印值不使用指数-toPlainString()科学记数法-toString()工程记数法-toEngineeringString()0.00010.00010.00010.00010.00000010.00000011E-7100E-9 注意toString()、toEngineeringString()方法在某些时候会使用科学计数法或工程计数法但是不是所有情况都会使用科学计数法或工程计数法的 对于一个数值可能很小的BigDecimal对象来说使用toString()可能由于打印的数值太小而打印其科学计数法表示而使用toPlainString()才能打印完整的数值。 package com.liudashuai;import java.math.BigDecimal;public class Test {public static void main(String[] args) {BigDecimal bg new BigDecimal(1E11);System.out.println(bg.toString()); // 1E11System.out.println(bg.toPlainString()); // 100000000000System.out.println(bg.toEngineeringString()); // 100E9BigDecimal b new BigDecimal(100000000000);System.out.println(b.toString()); // 1E11System.out.println(b.toPlainString()); // 100000000000System.out.println(b.toEngineeringString()); // 100E9} } 那么toString什么时候使用科学计数法呢 源码中toString方法上有很长的注释主要介绍指数计算转换过程简要总结了两种toString方法会以科学计数方式输出的场景。 先解释一下 unscaledValue 整数非标度值 即去掉小数点的Bigdecimal的值类型为BigInteger scale标度值如果为零或正数则标度是小数点后的位数。如果为负数则将该数的非标度值乘以 10 的负 scale 次幂。 如下BigDecimal值为123.00则unscaledValue 为12300而scale为2套用公式则数值是相等的。 场景一scale为负数一定会转换为科学计数的方式 场景二 需要先计算变动指数的值。公式为-scale(unscaleValue.length-1) ,如果该值小于-6那么则会使用科学计数的方式输出字符串。 package com.liudashuai;import java.math.BigDecimal;public class Test {public static void main(String[] args) {//案例一BigDecimal b1 new BigDecimal(0.000000123).setScale(9);System.out.println(b1.toString());System.out.println(b1.toPlainString());System.out.println(b1.scale());System.out.println(b1.unscaledValue());System.out.println();/*输出结果1.23E-70.0000001239123*///案例二BigDecimal b2 new BigDecimal(0.000001234).setScale(9);System.out.println(b2.toString());System.out.println(b2.toPlainString());System.out.println(b2.scale());System.out.println(b2.unscaledValue());System.out.println();/*输出结果0.0000012340.00000123491234*///案例三BigDecimal b3 new BigDecimal(0.123000000).setScale(9);System.out.println(b3.toString());System.out.println(b3.toPlainString());System.out.println(b3.scale());System.out.println(b3.unscaledValue());System.out.println();/*输出结果0.1230000000.1230000009123000000*///案例四BigDecimal b4 new BigDecimal(123000000);System.out.println(b4.toString());System.out.println(b4.toPlainString());System.out.println(b4.scale());System.out.println(b4.unscaledValue());System.out.println();/*输出结果1230000001230000000123000000*///案例五//Double d 12345678d; Double d 12345678.0; 效果一样Double d (double) 12345678;BigDecimal b5 BigDecimal.valueOf(d);System.out.println(d);System.out.println(b5.toString());System.out.println(b5.toPlainString());System.out.println(b5.scale());System.out.println(b5.unscaledValue());System.out.println();/*输出结果1.2345678E71234567812345678012345678*/} }注意这个unscaleValue、scale与BigDecimal是否压缩无关。所以toString是否以科学计数法来展示和是否压缩也无关就看-scale(unscaleValue.length-1)-6是否成立成立就展示科学计数法。 比如 package com.liudashuai;import java.math.BigDecimal;public class Test {public static void main(String[] args) {//这个值是会被压缩的但是不会unscaledValue()的值unscaledValue()的值还是不看小数点的值即31415926BigDecimal b1 new BigDecimal(0.000000226);System.out.println(b1.scale());System.out.println(b1.unscaledValue());System.out.println(b1.toString());//下面这个值不会被压缩的unscaledValue()还是不看小数点的值即31415926314159263141592631415926BigDecimal b2 new BigDecimal(0.00000000031415926314159263141592631415926);System.out.println(b2.scale());System.out.println(b2.unscaledValue());System.out.println(b2.toString());} }toEngineeringString()与工程计数法 如果一个BigDecimal对象执行toString()是以指数形式返回那么调用toEngineeringString()则以工程计数法返回只是要注意工程计数法返回的10的幂必须是3的倍数就行了。 除法要注意的事情 使用divide除法函数除不尽出现无线循环小数的时候会出现报错。报错为Exception in thread “main” java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result. package com.liudashuai;import java.math.BigDecimal;public class Test {public static void main(String[] args) {BigDecimal a new BigDecimal(10);BigDecimal b new BigDecimal(3);System.out.println(a.divide(b));} }注意如果你直接对一个数约定保留位数也一样会有上面的问题。 package com.liudashuai;import java.math.BigDecimal;public class Test {public static void main(String[] args) {BigDecimal b new BigDecimal(1.6666);System.out.println(result b: b.setScale(2, BigDecimal.ROUND_HALF_UP)); // 1.67System.out.println(result b: b.setScale(2)); // 精度错误} }BigDecimal的提供了八种舍入模式我们可以用这个来解决上面的问题下面例子都是假设保留0位小数哈。 1.ROUND_UP 舍入时尽量远离0有值就进行进位操作。舍入为一位时如下表 处理前处理后1.122.831.01-1.3-2-2.9-3-1.0-11.x2-1.x-2 总结无论正负上一位数字加1处理数非0时 2.ROUND_DOWN 舍入时尽量接近0有值就进行进位操作。舍入为一位时如下表 处理前处理后1.112.821.01-1.3-1-2.9-2-1.0-11.x1-1.x-1 总结无论正负上一位数字不变 3.ROUND_CEILING 舍入时接近正无穷方向即向数字大的方向有值时进行进位操作。舍入为一位时如下表 处理前处理后1.122.831.01-1.3-1-2.9-2-1.0-11.x2-1.x-1 总结正数时与 ROUND_UP 相同远离0上一位数字加1处理数非0时负数时与ROUND_DOWN相同靠近0上一位数字不变 4.ROUND_FLOOR 舍入时接近负无穷方向即向数字小的方向有值时进行进位操作。舍入为一位时如下表 处理前处理后1.112.821.01-1.3-2-2.9-3-1.0-11.x1-1.x-2 总结正数时与ROUND_DOWN相同靠近0上一位数字不变负数时与 ROUND_UP 相同远离0上一位数字加1处理数非0时 5.ROUND_HALF_UP 最经典的四舍五入。向最接近的数字方向进行取舍若为5两边一样近则向上取舍。舍入为一位时如下表 处理前处理后1.112.831.011.52-1.3-1-2.9-3-1.0-1-1.5-2 总结当处理位大于等于5时与 ROUND_UP 相同远离0上一位数字加1当处理位小于5时与ROUND_DOWN相同靠近0上一位数字不变 6.ROUND_HALF_DOWN 五舍六入类似于四舍五入向最接近的数字方向进行取舍。但与此不同的是若为5两边一样近则向下取舍。舍入为一位时如下表 处理前处理后1.112.831.011.51-1.3-1-2.9-3-1.0-1-1.5-1 总结与四舍五入类似。当处理位大于5时与 ROUND_UP 相同远离0上一位数字加1当处理位小于等于5时与ROUND_DOWN相同靠近0上一位数字不变 7.ROUND_HALF_EVEN 银行家舍入法四舍六入五成双五分两种情况。向最接近的数字方向进行取舍若为5根据5后面的数字来确定当5后有数时舍5进1当5后无有效数字时需要分两种情况来讲 15前一位为奇数舍5进1 25前一位为偶数舍5不进 如舍入为一位时下表举例 处理前处理后1.112.831.011.522.522.513-1.3-1-2.9-3-1.0-1-1.5-2-2.5-2-2.51-3 总结四舍六入当处理位大于5时与 ROUND_UP 相同远离0上一位数字加1当处理位小于5时与ROUND_DOWN相同靠近0上一位数字不变当处理位等于5时分情况进行取舍 8.ROUND_UNNECESSARY 进行断言要有精确的结果不进行舍入。如果不精确会抛出ArithmeticException如下表举例 处理前处理后1.1ArithmeticException2.8ArithmeticException1.011.5ArithmeticException-1.3ArithmeticException-2.9ArithmeticException-1.0-1-1.5ArithmeticException1.502ArithmeticException1.0001 总结只有精确时输出数字其余不精确时抛出ArithmeticException。总之就是要求保留的位数之后若还有数据那么就报错没有数据就不报错保留的位数之后是无意义的0也不报错。 这些舍入模式可以在下面两个方法中指定 public BigDecimal setScale(int newScale, RoundingMode roundingMode) int newScale 精确小数位RoundingMode roundingMode 舍入模式 public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) BigDecimal divisor 除数int scale 精确小数位int roundingMode 舍入模式 第二种方式适合在除的时候指定精确位数和舍入模式。 例子1保留0位小数我们测试一下上面ROUND_UP舍入模式的第一个例子 package com.liudashuai;import java.math.BigDecimal;public class Test {public static void main(String[] args) {BigDecimal b new BigDecimal(1.1);//保留两位小数0位小数之后的数使用BigDecimal.ROUND_UP进行舍入System.out.println(result b: b.setScale(0, BigDecimal.ROUND_UP));} }例子2保留多位小数 package com.liudashuai;import java.math.BigDecimal;public class Test {public static void main(String[] args) {BigDecimal b new BigDecimal(1.1431);//保留两位小数2位小数之后的数使用BigDecimal.ROUND_UP进行舍入System.out.println(result b: b.setScale(2, BigDecimal.ROUND_UP));} }例子3: package com.liudashuai;import java.math.BigDecimal;public class Test {public static void main(String[] args) {BigDecimal b new BigDecimal(1.6666);System.out.println(result b: b.setScale(2, BigDecimal.ROUND_HALF_UP)); // 1.67 四舍五入System.out.println(result b: b.setScale(2, BigDecimal.ROUND_DOWN)); // 1.66 向靠近0反向舍入System.out.println(result b: b.setScale(2, BigDecimal.ROUND_UP)); // 1.67 向远离0反向舍入} }还要注意一点 package com.liudashuai;import java.math.BigDecimal;public class Test {public static void main(String[] args) {BigDecimal b new BigDecimal(1.6666);System.out.println(result b: b.setScale(2, BigDecimal.ROUND_HALF_UP)); // 1.67System.out.println(result b: b.setScale(2)); // 精度错误} } setScale方法默认使用的roundingMode是ROUND_UNNECESSARY。这时你设置保留2位小数但是小数点后有4位所以会抛异常。这就是前面例子里抛出异常的原因。 例子4 使用public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)除的时候设置舍入规则又同时设置保留的小数位数。这样就不怕除不尽了抛出异常了。 package com.liudashuai;import java.math.BigDecimal;public class Test {public static void main(String[] args) {BigDecimal a new BigDecimal(10);BigDecimal b new BigDecimal(3);System.out.println(a.divide(b,2,BigDecimal.ROUND_DOWN));} }格式化展示BigDecimal值 这里不展开介绍DecimalFormat类的使用我们就记几个常用的就行了。这个DecimalFormat主要是用于格式输出数值用的一个类。这个类的用法可以自己百度。 这里举一个“千位符格式展示价格”的例子 package com.liudashuai;import java.math.BigDecimal; import java.text.DecimalFormat;public class Test {public static void main(String[] args) {BigDecimal p new BigDecimal(1299792458.124456);BigDecimal p2 new BigDecimal(1299792458.1260);BigDecimal p3 new BigDecimal(1299792458.1251);BigDecimal p4 new BigDecimal(1299792458.1250);BigDecimal p5 new BigDecimal(1299792458.1350);//每三位以逗号进行分隔。System.out.println(new DecimalFormat(,###.00).format(p));//1,299,792,458.12//每三位以逗号进行分隔。System.out.println(new DecimalFormat(,###.00).format(p2));//1,299,792,458.13//每三位以逗号进行分隔。System.out.println(new DecimalFormat(,###.00).format(p3));//1,299,792,458.13//每三位以逗号进行分隔。System.out.println(new DecimalFormat(,###.00).format(p4));//1,299,792,458.12//每三位以逗号进行分隔。System.out.println(new DecimalFormat(,###.00).format(p5));//1,299,792,458.14} }负数一样可以哈 依然是银行家算法四舍六入五考虑五后非零就进一五后为零看奇偶五前为偶应舍去五前为奇要进一 下面再举一个例子 package com.liudashuai;import java.math.BigDecimal; import java.text.DecimalFormat;public class Test {public static void main(String[] args) {DecimalFormat df new DecimalFormat();String stylenull;BigDecimal data new BigDecimal(1299792458.124465);// 在格式后添加单位字符style 00000.000 kg;df.applyPattern(style);System.out.println(采用style: style 格式化之后: df.format(data));// 模式中的%表示乘以100并显示为百分数要放在最后。style 0.00 %;df.applyPattern(style);System.out.println(采用style: style 格式化之后: df.format(data));// 模式中的\u2030表示乘以1000并显示为千分数要放在最后。style 0.00 \u2030;DecimalFormat df1 new DecimalFormat(style); //在构造函数中设置数字格式System.out.println(采用style: style 格式化之后: df1.format(data));// 嵌入文本中style 这件衣服的价格是 ##.00 元;df.applyPattern(style);System.out.println(采用style: style 格式化之后: df.format(data));// 嵌入文本中style 这件衣服的价格是 ,###.00 元;df.applyPattern(style);System.out.println(采用style: style 格式化之后: df.format(data));} }踩坑 BigDecimal.valueOf(double)方法如果你传的double位数不多那么是创建的BigDecimal表示的数是精确表示你指定的数的但是如果你传的double位数多创建的BigDecimal可能就不精确表示你指定的数了具体看下面例子所以建议使用new BigDecimal(String)方法创建对象。虽然阿里规约里面说可以使用BigDecimal.valueOf(double)但是我觉得不建议使用。 注意如果你调用BigDecimal的valueOf方法传的是一个float变量那么实际执行的还是BigDecimal.valueOf(double)方法相当于是double变量接受一个float变量而已是ok的。这里有一点你传一个float变量的时候你传一个位数少的float数创建的BigDecimal也不是精确表示你指定的数的具体如下。 package com.liudashuai;import java.math.BigDecimal;public class Test {public static void main(String[] args) {double a31.123456789123456789;System.out.println(Double.toString(a));//31.123456789123455System.out.println(BigDecimal.valueOf(a));//31.123456789123455System.out.println(BigDecimal.valueOf(a).add(new BigDecimal(0.000000000000004)));//实际结果应该是31.123456789123460但是显示31.123456789123459因为31.123456789123456789表示为BigDecimal为31.123456789123455精度丢失了。double b3.35;System.out.println(Double.toString(b));//3.35System.out.println(BigDecimal.valueOf(b)); //3.35。位数少时BigDecimal.valueOf(double)精确。float c 3.33f;System.out.println(Double.toString(c));//3.3299999237060547System.out.println(BigDecimal.valueOf(c)); //3.3299999237060547。位数少时BigDecimal.valueOf(float)也不精确。} }public static BigDecimal valueOf(double val) 方法的源码如下 其实就相当于是new BigDecimal(String)只是他是把你传过来的数先执行Double.toString(……)然后把返回值作为new BigDecimal(String)的参数去创建对象的如果你执行Double.toString(……)方法没有你想要的精确值那么创建的BigDecimal也不会精确这就是上面展示这个效果的原因了。所以还是建议直接使用new BigDecimal(String)创建BigDecimal对象。 等值比较的坑 一般在比较两个值是否相等时都是用equals 方法但是在BigDecimal 中使用equals可能会导致结果错误BigDecimal 中提供了 compareTo 方法在很多时候需要使用compareTo 比较两个值。如下所示 所以要比较两个BigDecimal值的大小尽量采用compareTo方法 无限精度的坑 BigDecimal 并不代表无限精度当在两个数除不尽的时候就会出现无限精度的坑如下所示 在官方文档中对该异常有如下说明 If the quotient has a nonterminating decimal expansion and the operation is specified to return an exact result, an ArithmeticException is thrown. Otherwise, the exact result of the division is returned, as done for other operations. 大致意思就是如果在除法divide运算过程中如果商是一个无限小数如 0.333…而操作的结果预期是一个精确的数字那么将会抛出ArithmeticException异常。 此种情况只需要在使用 divide方法时指定结果的精度和舍入模式即可 BigDecimal三种字符串输出的坑 可以看到三种方式输出的结果可能都不相同可能这个并不是预期的结果 BigDecimal 有三个方法可以转为相应的字符串类型切记不要用错 以下内容介绍java.math.BigDecimal下的三个toString方法的区别及用法 toPlainString() : 不使用任何指数。 toString() 有必要时使用科学计数法。 toEngineeringString()有必要时使用工程计数法。 工程记数法是一种工程计算中经常使用的记录数字的方法与科学技术法类似但要求10的幂必须是3的倍数 使用BigDecimal进行计算时参数不能为NULL 使用BigDecimal进行除法计算时被除数不能为0 除不尽的我们必须指定取舍模式和精确位数这样得到的值就不是那个实际的值了所以计算可能会出错这个要自己注意一下。 工具类 package com.liudashuai;import java.math.BigDecimal;/*** 用于高精确处理常用的数学运算*/ public class ArithmeticUtils {//默认除法运算精度private static final int DEF_DIV_SCALE 10;/*** 提供精确的加法运算** param v1 被加数* param v2 加数* return 两个参数的和*/public static double add(double v1, double v2) {BigDecimal b1 new BigDecimal(Double.toString(v1));BigDecimal b2 new BigDecimal(Double.toString(v2));return b1.add(b2).doubleValue();}/*** 提供精确的加法运算** param v1 被加数* param v2 加数* return 两个参数的和*/public static BigDecimal add(String v1, String v2) {BigDecimal b1 new BigDecimal(v1);BigDecimal b2 new BigDecimal(v2);return b1.add(b2);}/*** 提供精确的加法运算** param v1 被加数* param v2 加数* param scale 保留scale 位小数* return 两个参数的和*/public static String add(String v1, String v2, int scale) {if (scale 0) {throw new IllegalArgumentException(The scale must be a positive integer or zero);}BigDecimal b1 new BigDecimal(v1);BigDecimal b2 new BigDecimal(v2);return b1.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();}/*** 提供精确的减法运算** param v1 被减数* param v2 减数* return 两个参数的差*/public static double sub(double v1, double v2) {BigDecimal b1 new BigDecimal(Double.toString(v1));BigDecimal b2 new BigDecimal(Double.toString(v2));return b1.subtract(b2).doubleValue();}/*** 提供精确的减法运算。** param v1 被减数* param v2 减数* return 两个参数的差*/public static BigDecimal sub(String v1, String v2) {BigDecimal b1 new BigDecimal(v1);BigDecimal b2 new BigDecimal(v2);return b1.subtract(b2);}/*** 提供精确的减法运算** param v1 被减数* param v2 减数* param scale 保留scale 位小数* return 两个参数的差*/public static String sub(String v1, String v2, int scale) {if (scale 0) {throw new IllegalArgumentException(The scale must be a positive integer or zero);}BigDecimal b1 new BigDecimal(v1);BigDecimal b2 new BigDecimal(v2);return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();}/*** 提供精确的乘法运算** param v1 被乘数* param v2 乘数* return 两个参数的积*/public static double mul(double v1, double v2) {BigDecimal b1 new BigDecimal(Double.toString(v1));BigDecimal b2 new BigDecimal(Double.toString(v2));return b1.multiply(b2).doubleValue();}/*** 提供精确的乘法运算** param v1 被乘数* param v2 乘数* return 两个参数的积*/public static BigDecimal mul(String v1, String v2) {BigDecimal b1 new BigDecimal(v1);BigDecimal b2 new BigDecimal(v2);return b1.multiply(b2);}/*** 提供精确的乘法运算** param v1 被乘数* param v2 乘数* param scale 保留scale 位小数* return 两个参数的积*/public static double mul(double v1, double v2, int scale) {BigDecimal b1 new BigDecimal(Double.toString(v1));BigDecimal b2 new BigDecimal(Double.toString(v2));return round(b1.multiply(b2).doubleValue(), scale);}/*** 提供精确的乘法运算** param v1 被乘数* param v2 乘数* param scale 保留scale 位小数* return 两个参数的积*/public static String mul(String v1, String v2, int scale) {if (scale 0) {throw new IllegalArgumentException(The scale must be a positive integer or zero);}BigDecimal b1 new BigDecimal(v1);BigDecimal b2 new BigDecimal(v2);return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();}/*** 提供相对精确的除法运算当发生除不尽的情况时精确到* 小数点以后10位以后的数字四舍五入** param v1 被除数* param v2 除数* return 两个参数的商*/public static double div(double v1, double v2) {return div(v1, v2, DEF_DIV_SCALE);}/*** 提供相对精确的除法运算。当发生除不尽的情况时由scale参数指* 定精度以后的数字四舍五入** param v1 被除数* param v2 除数* param scale 表示表示需要精确到小数点以后几位。* return 两个参数的商*/public static double div(double v1, double v2, int scale) {if (scale 0) {throw new IllegalArgumentException(The scale must be a positive integer or zero);}BigDecimal b1 new BigDecimal(Double.toString(v1));BigDecimal b2 new BigDecimal(Double.toString(v2));return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();}/*** 提供相对精确的除法运算。当发生除不尽的情况时由scale参数指* 定精度以后的数字四舍五入** param v1 被除数* param v2 除数* param scale 表示需要精确到小数点以后几位* return 两个参数的商*/public static String div(String v1, String v2, int scale) {if (scale 0) {throw new IllegalArgumentException(The scale must be a positive integer or zero);}BigDecimal b1 new BigDecimal(v1);BigDecimal b2 new BigDecimal(v1);return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toString();}/*** 提供精确的小数位四舍五入处理** param v 需要四舍五入的数字* param scale 小数点后保留几位* return 四舍五入后的结果*/public static double round(double v, int scale) {if (scale 0) {throw new IllegalArgumentException(The scale must be a positive integer or zero);}BigDecimal b new BigDecimal(Double.toString(v));return b.setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();}/*** 提供精确的小数位四舍五入处理** param v 需要四舍五入的数字* param scale 小数点后保留几位* return 四舍五入后的结果*/public static String round(String v, int scale) {if (scale 0) {throw new IllegalArgumentException(The scale must be a positive integer or zero);}BigDecimal b new BigDecimal(v);return b.setScale(scale, BigDecimal.ROUND_HALF_UP).toString();}/*** 取余数** param v1 被除数* param v2 除数* param scale 小数点后保留几位* return 余数*/public static String remainder(String v1, String v2, int scale) {if (scale 0) {throw new IllegalArgumentException(The scale must be a positive integer or zero);}BigDecimal b1 new BigDecimal(v1);BigDecimal b2 new BigDecimal(v2);return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();}/*** 取余数 BigDecimal** param v1 被除数* param v2 除数* param scale 小数点后保留几位* return 余数*/public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) {if (scale 0) {throw new IllegalArgumentException(The scale must be a positive integer or zero);}return v1.remainder(v2).setScale(scale, BigDecimal.ROUND_HALF_UP);}/*** 比较大小** param v1 被比较数* param v2 比较数* return 如果v1 大于v2 则 返回true 否则false*/public static boolean compare(String v1, String v2) {BigDecimal b1 new BigDecimal(v1);BigDecimal b2 new BigDecimal(v2);int bj b1.compareTo(b2);boolean res;if (bj 0) {res true;} else {res false;}return res;} }总结 因为计算机采用二进制处理数据但是很多小数如0.1的二进制是一个无线循环小数而这种数字在计算机中是无法精确表示的。 所以人们采用了一种通过近似值的方式在计算机中表示于是就有了单精度浮点数和双精度浮点数等。 所以作为单精度浮点数的float和双精度浮点数的double在表示小数的时候只是近似值并不是真实值。 所以当使用BigDecimal(Double)创建一个的时候得到的BigDecimal是损失了精度的。 想要避免这个问题可以通过BigDecimal(String)的方式创建BigDecimal这样的情况下0.1就会被精确的表示出来。其表现形式是一个无标度数值1和一个标度1的组合。 当然这里我们还需要注意一下BigDecimal的一些坑哈。要掌握BigDecimal的API的使用。 注意 在需要精确的小数计算时再使用BigDecimalBigDecimal的性能比double和float差在处理庞大复杂的运算时尤为明显。故一般精度的计算没必要使用BigDecimal。尽量使用参数类型为String的构造函数。BigDecimal都是不可变的 在进行每一次四则运算时都会产生一个新的对象 老对象不知道计算后的值做加减乘除运算后结果是在新对象中的。
http://www.pierceye.com/news/634467/

相关文章:

  • 广州网站建设平台网站怎么做必须交钱吗
  • 做网站费免图片网站背景图网站
  • 上海电商网站开发公司门户网站建设 总结
  • 网站产品类别顺序如果修改wordpress多城市seo
  • 做网站托管的好处公司erp系统
  • 管局备案网站高端定制网站的特点
  • 成都极客联盟网站建设公司有没有帮别人做网站
  • 宝安专业网站设计公司公众号小程序怎么做
  • 郑州网站优化公司爱范儿 wordpress 主题
  • 电商网站建设书宣传片拍摄技巧
  • 珠海的门户网站有哪些app开发是什么专业
  • 网站建设推广报价简单网页素材
  • 建设企业官方网站的流程37玩手游官网平台
  • 南通网站建设方案开发网站建设运营公众号运营合同
  • 制作网站语言seo推广软件怎样
  • 企业网站建设的三种方式wordpress 导航高亮
  • 个人 建设图片分享网站网站开发设计步骤
  • 温州做阀门网站公司网站的建设时间怎么查
  • 好看的个人网站主页网站建设选择什么模式
  • 做内衣的网站校园网站建设网站
  • 学做网站论坛vip共享wordpress分类下文章排序
  • 文章内容网站系统网页编辑怎么打开
  • 建网站难吗查看关键词被搜索排名的软件
  • 同学会网站建设方案全免费无代码开发平台
  • 做网站给女朋友溧阳网站制作
  • 怎么注册电力建设公司网站wordpress用户注册邮箱验证
  • 用asp做的网站如何发布上海公司网站备案
  • 金华企业网站建设公司知识付费小程序源码
  • 网站建设十胜石公众号编辑器免费模板
  • 做网站用虚拟机还是服务器广东深广东深圳网站建设服务