Интервью: BigDecimal точно не теряет точность?

Предисловие

Все мы знаем, что переменные с плавающей запятой теряют точность при вычислении. Следующий фрагмент кода:

System.out.println (0,05 + 0,01); 
System.out.println (1.0 - 0.42); 
System.out.println (4.015 * 100); 
System.out.println (123,3 / 100);

输出: 
+0,060000000000000005 
0,5800000000000001 
401,49999999999994 
1,2329999999999999

Видно, что при выполнении арифметики с плавающей запятой в Java возникнет проблема потери точности. Тогда, если мы будем считать цены на сырьевые товары, возникнут проблемы. Очень вероятно, что у нас на руках 0,06 юаня, но мы не можем купить товар стоимостью 0,05 юаня и 0,01 юаня. Поскольку, как показано выше, сумма двух из них составляет 0,060000000000000005. Это, несомненно, очень серьезная проблема, особенно когда увеличивается параллелизм веб-сайтов электронной коммерции, проблемы будут огромными. Это может привести к невозможности размещения заказа или проблемам при согласовании. Итак, теперь мы можем использовать класс BigDecimal в Java для решения такого рода проблем.

Популяризируйте:

Точность float в Java составляет 6-7 значащих цифр. Точность double - 15-16 цифр.

API

Конструктор:

Конструктор Описание                       
  BigDecimal (int) Создает объект с целочисленным значением, заданным параметром.      
  BigDecimal (double) создает объект со значением двойной точности, заданным параметром.     
  BigDecimal (long) создает объект с длинным целым значением, указанным в параметре.     
  BigDecimal (String) создает объект со значением, представленным строкой, указанной параметром.


функция:

 Описание метода                          
  add (BigDecimal) Добавьте значения в объект BigDecimal, а затем верните этот объект. 
  subtract (BigDecimal) Вычтите значение в объекте BigDecimal, а затем верните этот объект. 
  multiply (BigDecimal) Умножьте значения в объекте BigDecimal, а затем верните этот объект. 
  div (BigDecimal) Разделите значение в объекте BigDecimal, а затем верните этот объект. 
  toString () Преобразует числовое значение объекта BigDecimal в строку.    
  doubleValue () возвращает значение в объекте BigDecimal как число с двойной точностью.   
  floatValue () возвращает значение в объекте BigDecimal как число с одинарной точностью.   
  longValue () возвращает значение в объекте BigDecimal как длинное целое число.    
  intValue () возвращает значение в объекте BigDecimal как целое число.


Например, из-за общих числовых типов double не может точно представлять числа, содержащие более 16 цифр.

Также теряется точность BigDecimal

Когда мы используем BigDecimal, имеет смысл использовать его конструктор BigDecimal (String) для создания объектов. Другие, такие как BigDecimal b = new BigDecimal (1), по-прежнему имеют проблему потери точности. Следующий код:

BigDecimal a = новый BigDecimal (1.01); 
BigDecimal b = новый BigDecimal (1.02); 
BigDecimal c = новый BigDecimal ("1.01"); 
BigDecimal d = новый BigDecimal ("1.02"); 
System.out.println (a.add (b)); 
System.out.println (c.add (d)); 

2.0300000000000000266453525910037569701671600341796875 
2.03


Видно, что потеря точности BigDecimal еще более чрезмерна. Однако при использовании переменных конструктора BigDecimal BigDecimal (String) такой проблемы нет. Причина кроется в принципах компьютерной композиции, и их кодировка определяет этот результат. Long может точно хранить 19 цифр, а double - только 16 цифр. Поскольку double имеет биты exp, он может хранить более 16 цифр, но это должно происходить за счет неточности низкого порядка. Если вам нужно точное хранение более 19 цифр, вы должны использовать BigInteger для экономии, конечно, некоторая производительность будет принесена в жертву. Поэтому, когда мы обычно используем BigDecimal для решения проблемы потери точности в коммерческих операциях, мы должны использовать его для создания конструктора, параметром которого является String при объявлении объекта BigDecimal.

В то же время этот принцип также упоминается в статьях «Эффективная Java и MySQL. Должен знать, что должен знать». Float и double можно использовать только для научных расчетов и инженерных расчетов. Нам нужно использовать BigDecimal в бизнес-вычислениях.

И официально мы дали инструкции из комментариев к исходному коду.Далее часть аннотаций к конструктору параметра типа double класса BigDecimal:

* The results of this constructor can be somewhat unpredictable.
     * One might assume that writing {@code new BigDecimal(0.1)} in
     * Java creates a {@code BigDecimal} which is exactly equal to
     * 0.1 (an unscaled value of 1, with a scale of 1), but it is
     * actually equal to
     * 0.1000000000000000055511151231257827021181583404541015625.
     * This is because 0.1 cannot be represented exactly as a
     * {@code double} (or, for that matter, as a binary fraction of
     * any finite length).  Thus, the value that is being passed
     * <i>in</i> to the constructor is not exactly equal to 0.1,
     * appearances notwithstanding.
       ……
        * When a {@code double} must be used as a source for a
     * {@code BigDecimal}, note that this constructor provides an
     * exact conversion; it does not give the same result as
     * converting the {@code double} to a {@code String} using the
     * {@link Double#toString(double)} method and then using the
     * {@link #BigDecimal(String)} constructor.  To get that result,
     * use the {@code static} {@link #valueOf(double)} method.
     * </ol>
public BigDecimal(double val) {
    this(val,MathContext.UNLIMITED);
}


第一段也说的很清楚它只能计算的无限接近这个数,但是无法精确到这个数。第二段则说,如果要想准确计算这个值,那么需要把double类型的参数转化为String类型的。并且使用BigDecimal(String)这个构造方法进行构造。 去获取结果。

正确运用BigDecimal

另外,BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象,由刚才我们所罗列的API也可看出。

在一般开发过程中,我们数据库中存储的数据都是float和double类型的。在进行拿来拿去运算的时候还需要不断的转化,这样十分的不方便。这里我写了一个工具类:

/**
 * @author: Ji YongGuang.
 * @date: 19:50 2017/12/14.
 */
public class BigDecimalUtil {

    private BigDecimalUtil() {

    }

    public static BigDecimal add(double v1, double v2) {// v1 + v2
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2);
    }

    public static BigDecimal sub(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2);
    }

    public static BigDecimal mul(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2);
    }

    public static BigDecimal div(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        // 2 = 保留小数点后两位   ROUND_HALF_UP = 四舍五入
        return b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP);// 应对除不尽的情况
    }
}


该工具类提供了double类型的基本的加减乘除运算。直接调用即可。

最后

感谢大家看到这里,文章有不足,欢迎大家指出;如果你觉得写得不错,那就给我一个赞吧。


рекомендация

отblog.51cto.com/14849432/2540536
рекомендация