BigDecimal использует ступенчатую яму

1. Введение

Класс API BigDecimal, предоставляемый Java в пакете java.math, используется для выполнения точных операций над числами, состоящими более чем из 16 эффективных цифр. Переменная двойной точности с плавающей запятой double может обрабатывать 16 значащих цифр, но в практических приложениях может потребоваться выполнение операций и обработка больших или меньших чисел.

В общем, мы можем напрямую использовать Float и Double для тех чисел, которым не нужна точная точность вычислений, но Double.valueOf(String) и Float.valueOf(String) потеряют точность. Поэтому в процессе разработки, если нам нужны точные результаты вычислений, мы должны использовать для работы класс BigDecimal.

То, что создает BigDecimal, является объектом, поэтому мы не можем использовать традиционные арифметические операторы, такие как +, -, *, / для непосредственного выполнения математических операций над его объектом, но должны вызывать соответствующий метод. Параметры в методе также должны быть объектами BigDecimal. Конструктор — это специальный метод класса, предназначенный для создания объектов, особенно объектов с параметрами.

2. Общий конструктор BigDecimal

1. Часто используемые конструкторы

  • BigDecimal(int)
    создает объект с целочисленным значением, указанным в параметре

  • BigDecimal(double)
    создает объект с двойным значением, указанным в параметре

  • BigDecimal(long)
    создает объект с длинным целочисленным значением, указанным в параметре

  • BigDecimal(String)
    создает объект с числовым значением, указанным параметром, в виде строки

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,10000000000000000055511151231257827021181583404541015625. Это связано с тем, что 0,1 не может быть представлено точно как двойное число (или, если уж на то пошло, как любая двоичная дробь конечной длины). Таким образом, значение, переданное в конструктор, не будет точно равно 0,1 (хотя очевидно равно этому значению).

2) Конструктор String полностью предсказуем: запись newBigDecimal("0.1") создаст BigDecimal, точно равный ожидаемому 0.1. Поэтому для сравнения обычно рекомендуется сначала использовать метод построения String.

3) Если в качестве источника BigDecimal необходимо использовать double, обратите внимание, что этот конструктор обеспечивает точное преобразование, он не дает тот же результат, что и при использовании сначала метода Double.toString(double), а затем с помощью конструктора BigDecimal(String), convert удвоить до строки. Чтобы получить этот результат, используйте статический метод valueOf(double).

3. Подробное объяснение общих методов BigDecimal

1. Общие методы

  • add(BigDecimal)
    Добавляет значения в объект BigDecimal и возвращает объект BigDecimal

  • subtract(BigDecimal)
    Вычесть значения из объекта BigDecimal и вернуть объект BigDecimal

  • умножить (BigDecimal)
    Умножить значения в объекте BigDecimal и вернуть объект BigDecimal

  • разделить(BigDecimal)
    Делит значения в объекте BigDecimal и возвращает объект BigDecimal

  • toString()
    преобразует значение объекта BigDecimal в строку

  • doubleValue()
    преобразует значение объекта BigDecimal в число двойной точности.

  • floatValue()
    преобразует значение в объекте BigDecimal в число с одинарной точностью.

  • longValue()
    преобразует значение объекта BigDecimal в длинное целое число.

  • intValue()
    преобразует значение объекта BigDecimal в целое число.

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

4.Большое десятичное форматирование

Поскольку метод 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

5. Общие исключения BigDecimal

Исключение произошло во время разделения

java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result

Анализ причин:

При делении с помощью метода разделения BigDecimal, когда нет целочисленного деления и возникает бесконечный цикл десятичных знаков, будет выдано исключение: java.lang.ArithmeticException: Незаканчивающееся десятичное расширение, нет точного представимого десятичного результата.

Решение:

Метод деления устанавливает точную десятичную точку, например: делим(ххххх,2)

6. Резюме BigDecimal

1. Резюме

Используйте BigDecimal, когда требуется точное десятичное вычисление.Производительность BigDecimal хуже, чем у double и float, особенно при работе с большими и сложными операциями. Поэтому нет необходимости использовать BigDecimal для расчетов общей точности. Попробуйте использовать конструкторы с типом параметра String.
BigDecimal неизменяем, и каждый раз при выполнении четырех арифметических операций будет генерироваться новый объект, поэтому не забудьте сохранить значение после операции при выполнении сложения, вычитания, умножения и деления.

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;
    }
}

Supongo que te gusta

Origin blog.csdn.net/weixin_43114209/article/details/131532741
Recomendado
Clasificación