Interview: Verliert BigDecimal definitiv nicht an Präzision?

Vorwort

Wir alle wissen, dass Gleitkommavariablen bei der Berechnung an Genauigkeit verlieren. Der folgende Code:

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

Es ist ersichtlich, dass beim Ausführen von Gleitkomma-Arithmetik in Java das Problem des Genauigkeitsverlusts auftritt. Wenn wir dann die Rohstoffpreise berechnen, entstehen Probleme. Es ist sehr wahrscheinlich, dass wir 0,06 Yuan in unseren Händen haben, aber wir können kein Produkt von 0,05 Yuan und 0,01 Yuan kaufen. Denn wie oben gezeigt, beträgt die Summe der beiden 0,060000000000000005. Dies ist zweifellos ein sehr ernstes Problem, insbesondere wenn die Parallelität von E-Commerce-Websites zunimmt, werden die Probleme enorm sein. Dies kann dazu führen, dass keine Bestellung aufgegeben wird oder Probleme bei der Abstimmung auftreten. Als nächstes können wir die BigDecimal-Klasse in Java verwenden, um diese Art von Problem zu lösen.

Popularisieren:

Die Genauigkeit von float in Java beträgt 6-7 signifikante Stellen. Die Genauigkeit von double beträgt 15-16 Stellen.

API

Konstrukteur:

Konstruktor Beschreibung                       
  BigDecimal (int) Erstellt ein Objekt mit dem durch den Parameter angegebenen ganzzahligen Wert.      
  BigDecimal (double) erstellt ein Objekt mit dem durch den Parameter angegebenen Wert mit doppelter Genauigkeit.     
  BigDecimal (long) erstellt ein Objekt mit dem vom Parameter angegebenen langen Integer-Wert.     
  BigDecimal (String) erstellt ein Objekt mit dem Wert, der durch den durch den Parameter angegebenen String dargestellt wird.


Funktion:

 Methode Beschreibung                          
  add (BigDecimal) Fügen Sie die Werte im BigDecimal-Objekt hinzu und geben Sie dieses Objekt zurück. 
  subtrahieren (BigDecimal) Subtrahieren Sie den Wert im BigDecimal-Objekt und geben Sie dieses Objekt zurück. 
  multiplizieren (BigDecimal) Multiplizieren Sie die Werte im BigDecimal-Objekt und geben Sie dieses Objekt zurück. 
  dividieren (BigDecimal) Teilen Sie den Wert im BigDecimal-Objekt und geben Sie dieses Objekt zurück. 
  toString () Konvertiert den numerischen Wert des BigDecimal-Objekts in eine Zeichenfolge.    
  doubleValue () gibt den Wert im BigDecimal-Objekt als Zahl mit doppelter Genauigkeit zurück.   
  floatValue () gibt den Wert im BigDecimal-Objekt als Zahl mit einfacher Genauigkeit zurück.   
  longValue () gibt den Wert im BigDecimal-Objekt als lange Ganzzahl zurück.    
  intValue () gibt den Wert im BigDecimal-Objekt als Ganzzahl zurück.


Zum Beispiel kann double aufgrund allgemeiner numerischer Typen Zahlen mit mehr als 16 Ziffern nicht genau darstellen.

BigDecimal-Präzision geht ebenfalls verloren

Wenn wir BigDecimal verwenden, ist es sinnvoll, den BigDecimal (String) -Konstruktor zum Erstellen von Objekten zu verwenden. Andere, wie BigDecimal b = new BigDecimal (1), haben immer noch das Problem des Genauigkeitsverlusts. Der folgende Code:

BigDecimal a = neues BigDecimal (1.01); 
BigDecimal b = neues BigDecimal (1.02); 
BigDecimal c = neues BigDecimal ("1.01"); 
BigDecimal d = neues BigDecimal ("1.02"); 
System.out.println (a.add (b)); 
System.out.println (c.add (d));

输出 :
2.0300000000000000266453525910037569701671600341796875 
2.03


Es ist zu erkennen, dass der Präzisionsverlust von BigDecimal noch größer ist. Bei Verwendung der BigDecimal-Konstruktervariablen BigDecimal (String) gibt es jedoch kein solches Problem. Der Grund ist in den Prinzipien der Computerzusammensetzung enthalten, und ihre Codierung bestimmt dieses Ergebnis. Long kann 19 Ziffern genau speichern, während Double nur 16 Ziffern speichern kann. Da double exp-Bits hat, kann es mehr als 16 Stellen speichern, muss jedoch auf Kosten einer Ungenauigkeit niedriger Ordnung gehen. Wenn Sie eine genaue Speicherung von mehr als 19 Stellen benötigen, müssen Sie BigInteger verwenden, um zu sparen. Natürlich wird die Leistung beeinträchtigt. Wenn wir also im Allgemeinen BigDecimal verwenden, um das Problem des Genauigkeitsverlusts bei kommerziellen Operationen zu lösen, müssen wir damit einen Konstruktor erstellen, dessen Parameter beim Deklarieren eines BigDecimal-Objekts String ist.

Gleichzeitig wird dieses Prinzip auch in Effective Java und MySQL Must Know Must Know erwähnt. Float und Double können nur für wissenschaftliche und technische Berechnungen verwendet werden. Wir müssen BigDecimal im Business Computing verwenden.

Und wir haben offiziell Anweisungen aus den Kommentaren des Quellcodes gegeben. Das Folgende ist ein Teil der Anmerkungen zum Konstruktor des Double-Type-Parameters der BigDecimal-Klasse:

* 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类型的基本的加减乘除运算。直接调用即可。

最后

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


Ich denke du magst

Origin blog.51cto.com/14849432/2540536
Empfohlen
Rangfolge