JavaはBigDecimalを用いて高精度計算を行う

9329 ワード

まず、次のコードの例を見ます.
System.out.println(0.05 + 0.01);
System.out.println(0.05 - 0.03);
System.out.println(1.025 * 100);
System.out.println(305.1 / 1000);

出力結果:
0.060000000000000005
0.020000000000000004
102.49999999999999
0.30510000000000004

Java言語では、floatとdoubleと、それに対応するパッケージクラスFloatとDoubleの2つの基本的な浮動小数点タイプがサポートされています.これらはいずれもIEEE 754規格に準拠しており、32ビット浮動小数点および64ビット二重精度浮動小数点のバイナリ小数点に対してバイナリ規格が定義されている.
IEEE 754は、浮動小数点数を科学記数法により底数2の小数で表す.IEEE浮動小数点数は1桁で数字を表す記号で、8桁で指数を表し、23桁で端数、すなわち小数部を表し、符号のある整数の指数としては正負の分があり、小数部は2進(底数2)小数で表す
浮動小数点値で正確な値を表示しない
いくつかの非整数値(数ドルや数セントなどの小数点)は正確である必要があります.浮動小数点数は正確な値ではないので、丸め誤差が発生します.したがって、浮動小数点数を用いて通貨量のような正確な数を表現しようとするのは良い考えではない.浮動小数点数を使用してドルとセントの計算を行うと、災害的な結果が得られます.浮動小数点数は測定値のような数値を表すのに用いるのが好ましく、このような値は最初からあまり正確ではない.
BigDecimalの使用
JDK 1.3からJava開発者は非整数:BigDecimalを表す別の数値表現法を持っている.BigDecimalは標準クラスで、コンパイラでは特にサポートする必要はありません.任意の精度の小数を表し、計算することができます.
加算、減算、乗算および除算の方法は、BigDecimal値に算術演算を提供する.BigDecimalオブジェクトは可変ではないため、これらのメソッドのそれぞれは新しいBigDecimalオブジェクトを生成します.したがって、BigDecimalは、オブジェクトの作成にかかるコストのため、多くの数学計算には適していませんが、小数を正確に表すために設計されています.通貨量のような正確な数値を探している場合は、BigDecimalがこのタスクに適しています.
構築BigDecimal数
BigDecimalの場合、いくつかの使用可能なコンストラクション関数があります.1つのコンストラクション関数は2精度浮動小数点数を入力とし,もう1つは整数と換算係数を入力とし,もう1つは小数のStringを入力として表す.BigDecimal(double)コンストラクション関数を使用するには、それを理解していないと計算中に丸め誤差が発生するため注意してください.整数またはStringに基づくコンストラクション関数を使用してください.
public class Test {
    public static void main(String[] args) {
        //            
        BigDecimal bd1 = new BigDecimal(0.5);
        BigDecimal bd2 = new BigDecimal(0.1);
        System.out.println(bd1.add(bd2));

        //  String      
        BigDecimal bd3 = new BigDecimal("0.5");
        BigDecimal bd4 = new BigDecimal("0.1");
        System.out.println(bd3.add(bd4));
    }
}

出力結果:
0.6000000000000000055511151231257827021181583404541015625
0.6

上のコードはそれぞれ
BigDecimal(double val)
BigDecimal(String val)

構造BigDecimal数は方式によって異なり,出力の結果は異なる.
最初の例に戻ると、ツールクラスは、加算減算と四捨五入を含む正確な浮動小数点数演算を提供します.
import java.math.BigDecimal;

public class ArithUtil {
    private static final int DEF_DIV_SCALE = 6; //         

    /**
     *          。
     *
     * @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 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 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();
    }

    /**
     *   (  )       ,          ,         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();
    }

    /**
     *               。
     *
     * @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));
        BigDecimal one = new BigDecimal("1");

        return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }
}

終了:
Javaプログラムで浮動小数点数と小数点数を使うのは罠に満ちている.浮動小数点数と小数点数は整数のように「規則に従う」のではなく、浮動小数点計算が必ず整数または正確な結果を生むとは仮定できない.浮動小数点演算は、計算が本来不正確な数値、例えば測定として保持することが望ましい.定点数(例えば、数ドルと数セント)を表す必要がある場合は、BigDecimalを使用します.