BigDecimal的使用全面总结

BigDecimal

BigDecimal可以表示任意大小,任意精度的有符号十进制数。所以不用怕精度问题,也不用怕大小问题,放心使用就行了。就是要注意的是,使用的时候有一些注意点。还有就是要注意避免创建的时候存在精度问题,尤其是不要使用new BigDecimal(double val)这个构造方法来创建BigDecimal对象,因为double本来就不精确。

使用场景

BigDecimal,相信对于很多人来说都不陌生,很多人都知道他的用法,这是一种java.math包中提供的一种可以用来进行精确运算的类型。

很多人都知道,在进行金额表示、金额计算等场景,不能使用double、float等类型,而是要使用对精度支持的更好的BigDecimal。

所以,很多支付、电商、金融等业务中,BigDecimal的使用非常频繁。

注意点

  1. 使用BigDecimal的equals方法并不能验证两个数是否真的相等
  2. 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 754(IEEE二进制浮点数算术标准)规范的主要思想。

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的精度不同,可以简单的理解为保留有效位数不同。

  1. float的精度是保证至少8位有效数字是准确的。
  2. double的精度是保证至少17位有效数字是准确的。

float和double的精度如下:

序号数据类型描述有效位
1float单精度8位
2double双精度17位

其实,java保存一个小数,是采用保留有效位数的一个近似小数,作为这个小数实际存储的值的。

比如:

double d=0.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(即,9223372036854775807),intVal为空,并且会用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个基本属性总结如下:

  1. BigDecimal是通过unscaled value和scale来构造,同时使用Long.MAX_VALUE作为我们是否压缩的阈值。
  2. 当unscaled value超过阈值时采用intVal字段存储unscaled value,intCompact字段存储Long.MIN_VALUE。
  3. 如果unscaled value没有超过阈值,对unscaled value进行压缩存储到long型的intCompact字段,并用于后续计算,而intVal字段则为空。
  4. scale字段存储标度,可以理解为unscaled value最后一位到实际值小数点的距离。如例1中对于3.1415926来说unscaled value为31415926,最后一位6到实际值的小数点距离为7,scale记为7;对于例3中手动设置precision的情况,unscaled value为31415926xxx159的最后一位9到实际值31415926xxx15900的小数点距离为2,由于在小数点左边scale则记为-2。
  5. precision字段记录的是unscaled value的数字个数,当手动指定MathContext并且指定的precision小于实际precision的时候,会要求进行rounding操作。

关于无标度值的压缩机制大家了解即可,不是本文的重点,大家只需要知道BigDecimal主要是通过一个无标度值和标度来表示的就行了。

BigDecimal的构造方法

我们都知道,想要创建一个对象,需要使用该类的构造方法,在BigDecimal中有很多个构造方法。

一般记下面这四个就行了:

public class BigDecimal extends Number implements Comparable<BigDecimal> {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));}
}

在这里插入图片描述

通过以上代码示例,我们发现,在使用BigDecimalequals方法对1和1.0进行比较的时候,有的时候是 true(当使用 int、double 定义 BigDecimal 时),有的时候是 false(当使用 String 定义 BigDecimal时)。

那么,为什么会出现这样的情况呢,我们先来看下BigDecimalequals方法。

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 bigDecimal=new BigDecimal("1.2");BigDecimal bigDecimal2=new BigDecimal("1.20");int rs = bigDecimal.compareTo(bigDecimal2);System.out.println(rs);BigDecimal bigDecimal3=new BigDecimal("1.21");BigDecimal bigDecimal4=new BigDecimal("1.20");int rs1 = bigDecimal3.compareTo(bigDecimal4);System.out.println(rs1);BigDecimal bigDecimal5=new BigDecimal("0.9");BigDecimal bigDecimal6=new BigDecimal("1.20");int rs2 = bigDecimal5.compareTo(bigDecimal6);System.out.println(rs2);/*rs = -1,表示bigdemical小于bigdemical2;rs = 0,表示bigdemical等于bigdemical2;rs = 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.0001
0.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()); // 1E+11System.out.println(bg.toPlainString()); // 100000000000System.out.println(bg.toEngineeringString()); // 100E+9BigDecimal b = new BigDecimal("100000000000");System.out.println(b.toString()); // 1E+11System.out.println(b.toPlainString()); // 100000000000System.out.println(b.toEngineeringString()); // 100E+9}
}

在这里插入图片描述

那么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.12
2.83
1.01
-1.3-2
-2.9-3
-1.0-1
1.x2
-1.x-2

总结:无论正负,上一位数字加1(处理数非0时)

2.ROUND_DOWN

舍入时尽量接近0,有值就进行进位操作。舍入为一位时如下表:

处理前处理后
1.11
2.82
1.01
-1.3-1
-2.9-2
-1.0-1
1.x1
-1.x-1

总结:无论正负,上一位数字不变

3.ROUND_CEILING

舍入时接近正无穷方向,即向数字大的方向,有值时进行进位操作。舍入为一位时如下表:

处理前处理后
1.12
2.83
1.01
-1.3-1
-2.9-2
-1.0-1
1.x2
-1.x-1

总结:正数时,与 ROUND_UP 相同,远离0,上一位数字加1(处理数非0时);负数时,与ROUND_DOWN相同,靠近0,上一位数字不变

4.ROUND_FLOOR

舍入时接近负无穷方向,即向数字小的方向,有值时进行进位操作。舍入为一位时如下表:

处理前处理后
1.11
2.82
1.01
-1.3-2
-2.9-3
-1.0-1
1.x1
-1.x-2

总结:正数时,与ROUND_DOWN相同,靠近0,上一位数字不变;负数时,与 ROUND_UP 相同,远离0,上一位数字加1(处理数非0时)

5.ROUND_HALF_UP

最经典的四舍五入。向最接近的数字方向进行取舍,若为5(两边一样近),则向上取舍。舍入为一位时如下表:

处理前处理后
1.11
2.83
1.01
1.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.11
2.83
1.01
1.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后无有效数字时,需要分两种情况来讲:

(1)5前一位为奇数,舍5进1

(2)5前一位为偶数,舍5不进

如舍入为一位时下表举例:

处理前处理后
1.11
2.83
1.01
1.52
2.52
2.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.1ArithmeticException
2.8ArithmeticException
1.01
1.5ArithmeticException
-1.3ArithmeticException
-2.9ArithmeticException
-1.0-1
-1.5ArithmeticException
1.502ArithmeticException
1.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 style=null;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));}
}

在这里插入图片描述

踩坑
  1. 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 a=31.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 b=3.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对象。

  2. 等值比较的坑

    一般在比较两个值是否相等时,都是用equals 方法,但是,在BigDecimal 中使用equals可能会导致结果错误,BigDecimal 中提供了 compareTo 方法,在很多时候需要使用compareTo 比较两个值。如下所示:

    在这里插入图片描述

    所以,要比较两个BigDecimal值的大小,尽量采用compareTo方法;

  3. 无限精度的坑

    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方法时指定结果的精度和舍入模式即可:

    在这里插入图片描述

  4. BigDecimal三种字符串输出的坑

    在这里插入图片描述

    可以看到三种方式输出的结果可能都不相同,可能这个并不是预期的结果 ,BigDecimal 有三个方法可以转为相应的字符串类型,切记不要用错:

    以下内容介绍java.math.BigDecimal下的三个toString方法的区别及用法

    toPlainString() : 不使用任何指数。
    toString() :有必要时使用科学计数法。
    toEngineeringString():有必要时使用工程计数法。 工程记数法是一种工程计算中经常使用的记录数字的方法,与科学技术法类似,但要求10的幂必须是3的倍数

  5. 使用BigDecimal进行计算时参数不能为NULL

    在这里插入图片描述

  6. 使用BigDecimal进行除法计算时被除数不能为0

    在这里插入图片描述

  7. 除不尽的我们必须指定取舍模式和精确位数,这样得到的值就不是那个实际的值了,所以计算可能会出错,这个要自己注意一下。

    在这里插入图片描述

工具类
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的使用。

注意:

  1. 在需要精确的小数计算时再使用BigDecimal,BigDecimal的性能比double和float差,在处理庞大,复杂的运算时尤为明显。故一般精度的计算没必要使用BigDecimal。
  2. 尽量使用参数类型为String的构造函数。
  3. BigDecimal都是不可变的, 在进行每一次四则运算时,都会产生一个新的对象 ,老对象不知道计算后的值,做加减乘除运算后,结果是在新对象中的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/172366.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring全面详解(学习总结)

Spring FrameWork一、 前言二、IOC(控制反转)2.1 对于IOC的理解2.2如何使用IOC2.3配置文件的解读2.4IOC容器创建bean的两种方式2.5从IOC容器中取bean2.6bean的属性如果包含特殊字符 三、DI(依赖注入)四、Spring中的bean五、Spring中的继承六、Spring的依赖七、Spring读取外部资…

【咕咕送书 | 第六期】深入浅出阐述嵌入式虚拟机原理,实现“小而能”嵌入式虚拟机!

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《粉丝福利》 《linux深造日志》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 ⛳️ 写在前面参与规则引言一、为什么嵌入式系统需要虚拟化技术&#xff1f;1.1 专家推荐 二、本书适合谁&#x…

WiFi的CSMA/CA竞争窗口流程简述

1、若站点最初有数据要发送&#xff08;不是发送不成功再进行重传的那种&#xff09;&#xff0c;且检测到信道空闲&#xff0c;在等待DIFS后&#xff0c;就发送整个数据帧。 2、否则&#xff0c;站点执行退避算法。一旦检测到信道忙&#xff0c;就冻结退避计时器。只要信道空…

Less 安装教程

文章目录 前言LESS的系统要求安装LESS例子输出Less编译css工具后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;Sass和Less &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努力填补技术短板…

error LNK2038: 检测到“RuntimeLibrary”的不匹配项 解决方法

问题&#xff1a; 我们在使用Visual Studio编程的时候偶尔会遇到以下三种报错&#xff1a; error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MD_DynamicRelease”不匹配值“MDd_DynamicDebug” &#xff08;引用的是release模式&#xff0c;但设置成debug模式了…

操作系统——进程管理

文章目录 进程和线程进程的概念进程和程序的区别PCB(进程控制块)程序是如何运行的进程的特征进程的状态和状态转换五态模型 进程控制进程状态装换为啥需要保证原子性如何实现原语的原子性&#xff1f; 进程控制相关原语进程创建进程终止进程的阻塞和唤醒进程的唤醒进程的切换 进…

1603. 整数集合划分(2016年408数据结构算法题)

一、题目 1603. 整数集合划分https://www.acwing.com/problem/content/description/1605/ 二、算法的基本设计思想 由题意知&#xff0c;将最小的 个元素放在 中&#xff0c;其余的元素放在 中&#xff0c;分组结果即可满足题目要求。仿照快速排序的思想&#xff0c;基于枢…

Vue 2.0源码分析-Virtual DOM

Virtual DOM 这个概念相信大部分人都不会陌生&#xff0c;它产生的前提是浏览器中的 DOM 是很“昂贵"的&#xff0c;为了更直观的感受&#xff0c;我们可以简单的把一个简单的 div 元素的属性都打印出来&#xff0c;如图所示&#xff1a; 可以看到&#xff0c;真正的 DOM …

地铁在线售票vue票务系统uniAPP+vue 微信小程序

功能介绍 管理员 &#xff08;1&#xff09;管理员登录功能 &#xff08;2&#xff09;查看和修改线路信息 &#xff08;3&#xff09;减少线路 &#xff08;4&#xff09;修改价格&#xff08;5站3元 5-10 5元 10-15站6元 往上8元&#xff09; &#xff08;5&#xff09;删除用…

吾爱破解置顶的“太极”,太好用了吧!

日常工作和娱乐&#xff0c;都需要用到不同类型的软件&#xff0c;哪怕软件体积不大&#xff0c;也必须安装&#xff0c;否则到用时找不到就非常麻烦了。 其实&#xff0c;很多软件不一定一样不剩地全部安装一遍&#xff0c;一方面原因是用的不多&#xff0c;另一方面多少有点…

Android设计模式--外观模式

弈之为术&#xff0c;在人自悟 一&#xff0c;定义 外观模式要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。提供一个高层次的接口&#xff0c;使得子系统更易于使用。 外观模式在开发中的使用频率是非常高的&#xff0c;尤其是在第三方的SDK里面&#xff0…

Django之admin页面样式定制(Simpleui)

好久不见&#xff0c;各位it朋友们&#xff01; 本篇文章我将向各位介绍Django框架中admin后台页面样式定制的一个插件库&#xff0c;名为Simpleui。 一&#xff09;简介 SimpleUI是一款简单易用的用户界面&#xff08;UI&#xff09;库&#xff0c;旨在帮助开发人员快速构建…

清华提出 SoRA,参数量只有 LoRA 的 70%,表现更好!

现在有很多关于大型语言模型&#xff08;LLM&#xff09;的研究&#xff0c;都围绕着如何高效微调展开。微调是利用模型在大规模通用数据上学到的知识&#xff0c;通过有针对性的小规模下游任务数据&#xff0c;使模型更好地适应具体任务的训练方法。 在先前的工作中&#xff…

基于驾驶训练算法优化概率神经网络PNN的分类预测 - 附代码

基于驾驶训练算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于驾驶训练算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于驾驶训练优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

Robots 元标签与 X-Robots 标签

Robots Meta Tag 和 X-Robots-Tag 是两个常用的 HTML 标签&#xff0c;它们对观察机动爬虫和其他网络机器人很有启发性。这些标签可以控制您的网页如何被记录和显示。 什么是机器人元标记&#xff1f; 机器人元标记是一个 HTML 标签&#xff0c;它提供信息来查看电机爬虫和其…

Figma 插件学习(二)- 常用属性和方法

一. 如何调试figma插件 Plugins > Development > Show/Hide console 打开控制台即可开始调试 二.节点类型 根据不同的节点类型&#xff0c;也是会有不同的方法和属性&#xff0c;介绍几个常用节点类型 1.FrameNode 框架节点是用于定义布局层次结构的容器。它类似于HTM…

GWAS:plink进行meta分析

之前教程提到过Metal是可以做Meta分析&#xff0c;除了Metal&#xff0c;PLINK也可以进行Meta分析。 命令如下所示&#xff1a; plink --meta-analysis gwas1.plink gwas2.plink gwas3.plink logscale qt --meta-analysis-snp-field SNP --meta-analysis-chr-field CHR --me…

BrokerChain

BrokerChain: A Cross-Shard Blockchain Protocol for Account/Balance-based State Sharding 我总感觉这篇文章不完整&#xff0c;缺少一些东西。或者说有些地方并没有详细说。比如状态图的构建&#xff0c;网络重分片的的配置过程。都直接忽略了。 Motivation 1 跨片交易不…

流程图是什么,用什么软件做?

在工作流程中&#xff0c;经常会遇到需要图形化呈现整个流程的情况。流程图就是一种一目了然的图形化表现方式&#xff0c;便于人们理解、沟通和管理整个流程。 1.Visio Visio是一款微软公司的图表软件&#xff0c;可以用于创建各种类型的流程图、组织结构图、网络图、平面图…

编译原理词法分析器

算法描述 对于给出的源代码&#xff0c;我们按行将其读入&#xff0c;对于每一行单独进行词法分析。 过滤行前后空格对字符串进行词语的分割 有空格则把空格前的字符归为一个词比较上一个字符和当前字符是否需要进行分割 检查词语是否合法词语合法则按 [待测代码中的单词符号…