1. Обзор BigDecimal
Класс API BigDecimal, предоставляемый Java в пакете java.math, используется для выполнения точных операций над числами, имеющими более 16 значащих цифр. Переменная двойной точности с плавающей запятой double может обрабатывать 16-разрядные значащие числа, но в практических приложениях может потребоваться выполнение операций и обработка больших или меньших чисел. В общем, для тех чисел, которым не нужна точная точность вычислений, мы можем использовать Float и Double напрямую, но Double.valueOf(String) и Float.valueOf(String) потеряют точность. Так что в разработке, если нам нужен результат точного расчета, мы должны использовать для работы класс BigDecimal.
BigDecimal создает объект, поэтому мы не можем использовать традиционные +, -, *, / и другие арифметические операторы для непосредственного выполнения математических операций над его объектом, но должны вызывать соответствующий метод. Параметры в методе также должны быть объектами BigDecimal. Конструкторы — это специальные методы классов, предназначенные для создания объектов, особенно объектов с параметрами.
2. Общие конструкторы BigDecimal
2.1 Общие конструкторы
-
BigDecimal(int)
Создает объект с целочисленным значением, указанным в параметре
-
BigDecimal(double)
Создает объект с двойным значением, указанным в параметре
-
BigDecimal(long)
Создает объект с длинным значением, указанным параметром
-
BigDecimal(String)
Создает объект с числовым значением, указанным параметром, в виде строки
2.2, используйте анализ проблемы
Пример использования:
BigDecimal a =new BigDecimal(0.1);
System.out.println("a values is:"+a);
System.out.println("=====================");
BigDecimal b =new BigDecimal("0.1");
System.out.println("b values is:"+b);
Пример результата:
a values is:0.1000000000000000055511151231257827021181583404541015625
=====================
b values is:0.1
Анализ причин:
1) Результат конструктора с типом параметра double непредсказуем. Можно подумать, что запись newBigDecimal(0.1) в Java создает BigDecimal, который точно равен 0,1 (немасштабированное значение 1, имеющее масштаб 1), но на самом деле он равен 0,100000000000000000055511151231257827021181583404541015625. Это связано с тем, что 0,1 не может быть представлено точно как двойное число (или, в данном случае, как любая двоичная дробь конечной длины). Таким образом, значение, переданное конструктору, не будет точно равно 0,1 (хотя якобы равно этому значению).
2) Конструктор String полностью предсказуем: запись newBigDecimal("0.1") создаст BigDecimal, точно равный ожидаемому 0.1. Поэтому для сравнения обычно рекомендуется использовать конструктор String.
3) Если в качестве источника BigDecimal необходимо использовать double, обратите внимание, что этот конструктор обеспечивает точное преобразование, а не тот же результат, что и при использовании сначала метода Double.toString(double), а затем конструктора BigDecimal(String) для преобразования double нанизывать. Чтобы получить этот результат, используйте статический метод valueOf(double).
3. Подробное объяснение часто используемых методов BigDecimal
3.1 Общие методы
-
add(BigDecimal)
Добавьте значения в объект BigDecimal, возвращая объект BigDecimal
-
subtract(BigDecimal)
Вычтите значения в объекте BigDecimal и верните объект BigDecimal
-
multiply(BigDecimal)
Умножение значений в объекте BigDecimal возвращает объект BigDecimal
-
divide(BigDecimal)
Разделите значения в объекте BigDecimal и верните объект BigDecimal
-
toString()
Преобразование значения в объекте BigDecimal в строку
-
doubleValue()
Преобразуйте значение в объекте BigDecimal в двойное
-
floatValue()
Преобразуйте значение в объекте BigDecimal в число с одинарной точностью.
-
longValue()
Преобразование значения в объекте BigDecimal в длинное целое число
-
intValue()
Преобразуйте значение в объекте BigDecimal в целое число.
3.2, Сравнение размеров BigDecimal
Метод compareTo для bigdemical обычно используется для сравнения размера BigDecimal в java.
int a = bigdemical.compareTo(bigdemical2)
Анализ результатов возврата:
a = -1,表示bigdemical小于bigdemical2;
a = 0,表示bigdemical等于bigdemical2;
a = 1,表示bigdemical大于bigdemical2;
Пример: a больше или равно b
new bigdemica(a).compareTo(new bigdemical(b)) >= 0
В-четвертых, форматирование BigDecimal
Поскольку метод format() класса NumberFormat может использовать объект BigDecimal в качестве своего параметра, вы можете использовать BigDecimal для управления форматированием значений валюты, процентных значений и общих значений, которые превышают 16 значащих цифр.
В качестве примера возьмем использование BigDecimal для валютного и процентного форматирования. Во-первых, создайте объект BigDecimal, после выполнения арифметической операции BigDecimal установите ссылки на форматирование валюты и процента соответственно и, наконец, используйте объект BigDecimal в качестве параметра метода format() для вывода его отформатированного значения валюты и процента.
NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立货币格式化引用
NumberFormat percent = NumberFormat.getPercentInstance(); //建立百分比格式化引用
percent.setMaximumFractionDigits(3); //百分比小数点最多3位
BigDecimal loanAmount = new BigDecimal("15000.48"); //贷款金额
BigDecimal interestRate = new BigDecimal("0.008"); //利率
BigDecimal interest = loanAmount.multiply(interestRate); //相乘
System.out.println("贷款金额:\t" + currency.format(loanAmount));
System.out.println("利率:\t" + percent.format(interestRate));
System.out.println("利息:\t" + currency.format(interest));
результат:
贷款金额: ¥15,000.48 利率: 0.8% 利息: ¥120.00
Формат BigDecimal сохраняет 2 как десятичное число, если его недостаточно, оно заполняется 0:
public class NumberFormat {
public static void main(String[] s){
System.out.println(formatToNumber(new BigDecimal("3.435")));
System.out.println(formatToNumber(new BigDecimal(0)));
System.out.println(formatToNumber(new BigDecimal("0.00")));
System.out.println(formatToNumber(new BigDecimal("0.001")));
System.out.println(formatToNumber(new BigDecimal("0.006")));
System.out.println(formatToNumber(new BigDecimal("0.206")));
}
/**
* @desc 1.0~1之间的BigDecimal小数,格式化后失去前面的0,则前面直接加上0。
* 2.传入的参数等于0,则直接返回字符串"0.00"
* 3.大于1的小数,直接格式化返回字符串
* @param obj传入的小数
* @return
*/
public static String formatToNumber(BigDecimal obj) {
DecimalFormat df = new DecimalFormat("#.00");
if(obj.compareTo(BigDecimal.ZERO)==0) {
return "0.00";
}else if(obj.compareTo(BigDecimal.ZERO)>0&&obj.compareTo(new BigDecimal(1))<0){
return "0"+df.format(obj).toString();
}else {
return df.format(obj).toString();
}
}
}
Результат:
3.44
0.00
0.00
0.00
0.01
0.21
Пять, общие исключения BigDecimal
5.1 Исключение возникает при делении
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result
Анализ причин:
При делении с помощью метода разделения BigDecimal будет выдано исключение, если существует бесконечный цикл десятичных дробей: java.lang.ArithmeticException: Незаканчивающееся десятичное расширение, нет точного представимого десятичного результата.
Решение:
Метод разделения устанавливает точную десятичную точку, например: разделить(ххххх,2)
Шесть, резюме BigDecimal
6.1. Резюме
- Используйте BigDecimal, когда вам нужны точные десятичные вычисления.Производительность BigDecimal хуже, чем у double и float, особенно при работе с большими и сложными операциями. Поэтому нет необходимости использовать BigDecimal для расчетов общей точности.
- Попробуйте использовать конструктор с типом параметра String.
- BigDecimal является неизменяемым (неизменяемым), новый объект будет генерироваться каждый раз при выполнении четырех операций, поэтому не забывайте сохранять значение после операции при выполнении сложения, вычитания, умножения и деления.
6.2, рекомендации по инструменту
package com.vivo.ars.util;
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;
}
}