【Java入門】BigDecimalで小数点以下を誤差なく計算する方法

こんにちは!フリーランスの桃太郎です。

Javaには誤差が出ないように四則演算するためのクラスとしてBigDecimalがあります。

この記事では、

・BigDecimalとは
・BigDecimalの使い方

という基本的な内容から、

・BigDecimalで四捨五入する方法
・BigDecimalにて消費税の計算をする方法

などの応用的な使い方に関しても解説していきます。

BigDecimalについて正しく理解し、必要な場面で使いこなすことができるように、わかりやすく解説します!

BigDecimalとは

BigDecimalとはJavaのクラスのひとつで、小数点以下の数値を含めた実数を取り扱います。

クラスの内部では実数を”整数”と”小数点の位置”という2つの情報に分けて管理しています。

また、BigDecimal同士の値の比較方法については以下の記事を参考にしてください。

【Java入門】BigDecimalの大小をcompareToで比較する方法
更新日 : 2019年4月16日

BigDicimalで小数点以下を計算する方法

銀行の預金金利やモノを買うときに発生する消費税など、小数点以下の数字をあつかうITシステムは多く存在します。

それらのシステムでは1円の誤差も許されない正確な計算が必要となります。

そのような場合に使用するのがBigDecimalです。

Javaでは小数点以下を扱えるdouble/floatといった他のクラスも存在します。

ただし、double/floatは誤差が生じる可能性があります。

正確な計算が必要な場面ではBigDecimalを使用しましょう。

そして、BigDecimalでは四則演算を行う場合、”+”や”-“といった算術演算子ではなく専用のメソッドが用意されています。

メソッド説明
add()足し算
subtract()引き算
multiply()掛け算
divide()割り算

BigDecimalクラスを使用するには、以下のパッケージをインポートします。

import java.math.BigDecimal;

例えば、以下のようにインスタンスを作成して使用します。

String val = “123.456789”;
BigDecimal bd = new BigDecimal(val);

足し算

足し算を行う場合は addメソッドを使用します。

addメソッドの引数には足し合わせたい値を入れます。

戻り値は足し合わせた結果がBigDecimal型で返ります。

BigDecimalのコンストラクタの引数に浮動小数点数型文字列型どちらも指定することができます。

しかし、引数に浮動小数点数型を指定した場合、誤差が発生するので注意が必要です。

正確な値を扱うためには、BigDecimalのコンストラクタの引数は文字列型で指定しましょう!

import java.math.BigDecimal;

public class Main {
 
    public static void main(String[] args) {
        // double型で算術演算子を使う場合
        double d1 = 0.8;
        double d2 = 0.9;
        System.out.println(d1 + " + " + d2 + " = " + (d1 + d2));
        
        // BigDecimalコンストラクタの引数にdouble型を使用する場合
        BigDecimal bd1 = new BigDecimal(0.8);
        BigDecimal bd2 = new BigDecimal(0.9);
        BigDecimal result1 = bd2.add(bd1);
        System.out.println(bd1 + " + " + bd2 + " = " + result1);
        
        // BigDecimalコンストラクタの引数にString型を使用する場合
        BigDecimal bd3 = new BigDecimal("0.8");
        BigDecimal bd4 = new BigDecimal("0.9");
        BigDecimal result2 = bd4.add(bd3);
        System.out.println(bd3 + " + " + bd4 + " = " + result2);
    }

}

実行結果:

0.8 + 0.9 = 1.7000000000000002
0.8000000000000000444089209850062616169452667236328125 + 0.90000000000000002220446049250313080847263336181640625 = 1.70000000000000006661338147750939242541790008544921875
0.8 + 0.9 = 1.7

このサンプルコードでは、double型で算術演算子を使った場合と、BigDecimal型でコンストラクタの引数にdouble型とString型を使った場合で足し算の結果を比較しています。

double型で算術演算子を使った場合とBigDecimal型でコンストラクタの引数にdouble型を使った場合は誤差が生じて正確な値が算出できていません

これに対して、BigDecimal型でコンストラクタの引数にString型を使った場合は誤差は生じず、正確な値を算出できています。

引き算

引き算を行う場合は subtractメソッドを使用します。

import java.math.BigDecimal;

public class Main {
 
    public static void main(String[] args) {
        // double型で算術演算子を使う場合
        double d1 = 1.7;
        double d2 = 0.9;
        System.out.println(d1 + " - " + d2 + " = " + (d1 - d2));
        
        // BigDecimalコンストラクタの引数にdouble型を使用する場合
        BigDecimal bd1 = new BigDecimal(1.7);
        BigDecimal bd2 = new BigDecimal(0.9);
        BigDecimal result1 = bd1.subtract(bd2);
        System.out.println(bd1 + " - " + bd2 + " = " + result1);
        
        // BigDecimalコンストラクタの引数にString型を使用する場合
        BigDecimal bd3 = new BigDecimal("1.7");
        BigDecimal bd4 = new BigDecimal("0.9");
        BigDecimal result2 = bd3.subtract(bd4);
        System.out.println(bd3 + " - " + bd4 + " = " + result2);    
    }

}

実行結果:

1.7 - 0.9 = 0.7999999999999999
1.6999999999999999555910790149937383830547332763671875 - 0.90000000000000002220446049250313080847263336181640625 = 0.79999999999999993338661852249060757458209991455078125
1.7 - 0.9 = 0.8

このサンプルコードでも、double型で算術演算子を使った場合とBigDecimal型でコンストラクタの引数にdouble型を使った場合は誤差が生じて正確な値が算出できていません。

これに対して、BigDecimal型でコンストラクタの引数にString型を使った場合は誤差は生じず、正確な値を算出できています。

掛け算

掛け算を行う場合は multiplyメソッドを使用します。

import java.math.BigDecimal;

public class Main {
 
    public static void main(String[] args) {
        // double型で算術演算子を使う場合
        double d1 = 0.8;
        double d2 = 0.9;
        System.out.println(d1 + " x " + d2 + " = " + (d1 * d2));
        
        // BigDecimalコンストラクタの引数にdouble型を使用する場合
        BigDecimal bd1 = new BigDecimal(0.8);
        BigDecimal bd2 = new BigDecimal(0.9);
        BigDecimal result1 = bd1.multiply(bd2);
        System.out.println(bd1 + " x " + bd2 + " = " + result1);
        
        // BigDecimalコンストラクタの引数にString型を使用する場合
        BigDecimal bd3 = new BigDecimal("0.8");
        BigDecimal bd4 = new BigDecimal("0.9");
        BigDecimal result2 = bd3.multiply(bd4);
        System.out.println(bd3 + " x " + bd4 + " = " + result2);    
    }

}

実行結果:

0.8 x 0.9 = 0.7200000000000001
0.8000000000000000444089209850062616169452667236328125 x 0.90000000000000002220446049250313080847263336181640625 = 0.720000000000000057731597280508141088104978266987413014660706603482787091508043886278755962848663330078125
0.8 x 0.9 = 0.72

このサンプルコードでも、double型で算術演算子を使った場合とBigDecimal型でコンストラクタの引数にdouble型を使った場合は誤差が生じて正確な値が算出できていません

これに対して、BigDecimal型でコンストラクタの引数にString型を使った場合は誤差は生じず、正確な値を算出できています。

割り算

割り算を行う場合は divideメソッドを使用します。

割り算の場合は気をつけるべき点があります。

たとえば、10 ÷ 3 = 3.33333… のような結果が割り切れない値の場合、ArithmeticExceptionというエラーが発生してしまいます。

それを回避するために、BigDecimalでは各メソッドの引数に第二引数以降が用意されています。

その第3引数に計算結果の値の丸め方法を指定することで、エラーを回避できます。

ちなみに第2引数では小数点以下の桁数を指定します。

import java.math.BigDecimal;

public class Main {
 
    public static void main(String[] args) {
        // double型で算術演算子を使う場合
        double d1 = 10.0;
        double d2 = 3.0;
        System.out.println(d1 + " ÷ " + d2 + " = " + (d1 / d2));
        
        // BigDecimalコンストラクタの引数にdouble型を使用する場合
        BigDecimal bd1 = new BigDecimal(10.0);
        BigDecimal bd2 = new BigDecimal(3.0);
        BigDecimal result1 = bd1.divide(bd2, 1, BigDecimal.ROUND_HALF_UP);
        System.out.println(bd1 + " ÷ " + bd2 + " = " + result1);
        
        // BigDecimalコンストラクタの引数にString型を使用する場合
        BigDecimal bd3 = new BigDecimal("10.0");
        BigDecimal bd4 = new BigDecimal("3.0");
        BigDecimal result2 = bd3.divide(bd4, 1, BigDecimal.ROUND_HALF_UP);
        System.out.println(bd3 + " ÷ " + bd4 + " = " + result2);    
    }

}

実行結果:

10.0 ÷ 3.0 = 3.3333333333333335
10 ÷ 3 = 3.3
10.0 ÷ 3.0 = 3.3

このサンプルコードでは、double型で算術演算子を使った場合は誤差が生じて正確な値が算出できていません

これに対して、BigDecimal型を使った場合は誤差は生じず、正確な値を算出できています。

BigDecimalで四捨五入する方法

BigDecimalで四捨五入する方法については以下の記事を参考にしてください。

【解決Java】BigDecimalで四捨五入や切り捨ての丸め誤差を解決
更新日 : 2019年4月5日

BigDecimalで消費税を計算する方法

900円の商品に対する消費税を含めた代金は 900 × 1.08 = 972円と計算ができます。

これをJavaのプログラミング上で計算をおこなう場合、double型で算術演算子を使って計算する方法と、BigDecimalクラスを使う方法があります。

import java.math.BigDecimal;

public class Main {
 
    public static void main(String[] args) {
        // double型で算術演算子を使う場合
        double price = 900;
        double tax = 1.08;
        double result = price * tax;
        System.out.println(result);
        
        // BigDecimalコンストラクタの引数にdouble型を使用する場合
        BigDecimal price1 = new BigDecimal(900);
        BigDecimal tax1 = new BigDecimal(1.08);
        BigDecimal result1 = price1.multiply(tax1);
        System.out.println(result1);
        
        // BigDecimalコンストラクタの引数にString型を使用する場合
        BigDecimal price2 = new BigDecimal("900");
        BigDecimal tax2 = new BigDecimal("1.08");
        BigDecimal result2 = price2.multiply(tax2);
        System.out.println(result2);    
    }

}

実行結果:

972.0000000000001
972.0000000000000639488462184090167284011840820312500
972.00

double型で算術演算子を使って計算した場合とBigDecimal型でコンストラクタの引数にdouble型を使った場合は誤差が発生しています。

わずかな差に見えますが、これがシステムの要件で1円以下は切り上げと決めていた場合は代金が973円となり、1円多くお客様へ請求することになります。

そして、そのことがサービスの信頼性の低下につながり、オンラインショップの場合では閉店に追い込まれる原因になる可能性もあります。

BigDecimal型でコンストラクタの引数にString型を使った場合、正確な計算結果が得られています。

BigDecimalの使い方総まとめ記事はこちら

BigDecimalのさまざまな使い方についてはこちらで詳しく解説しているので、ぜひ確認してください!

【Java入門】BigDecimalの使い方総まとめ(足し算、引き算などの計算)
更新日 : 2019年3月29日

演算子(算術演算子)について知りたい方へ

算術演算子についてはこちらで詳しく解説しているので、ぜひ確認してください!

【Javaの演算子】種類や使い方を網羅しました!
更新日 : 2018年10月4日

日付の計算について詳しく知りたい方へ

また、日付の計算についても算術演算子を使うのではなく、メソッドが用意されています。

日付の計算についてはこちらで詳しく解説しているので、ぜひ確認してください!

【Java入門】Dateクラスで現在日時の取得と加算/減算などの計算(add)
更新日 : 2019年5月22日

まとめ

ここでは、BigDecimalについて、基本的な使い方から消費税を計算する実務的な具体例についても紹介しました。

小数点以下の数字を扱うシステムは数多く存在します。

一見すると計算結果に問題ないように見えても、正確な計算が必要な場合にはdouble/floatではなく、BigDecimalを使うようにしてください。

もし、BigDecimalの使い方を忘れてしまったら、この記事を思い出してくださいね!

LINEで送る
Pocket

SEからWebエンジニアへ転職した理由

侍エンジニア塾卒業生の小池さんは、以前は社内SEとして約5年ほど勤務していました。しかし業務内容は社内のヘルプデスク対応など、プログラムを書く仕事は全くなかったそうです。

SEながらプログラムを書けない現状に「将来仕事がなくなるんじゃないか」と不安を感じ、プログラミング学習を決意。

弊社スクールで学習し、無事ベンチャー企業のプログラマーとして転職に成功しました。そんな小池さんの学習法や転職体験談を伺いましたので、是非ご覧ください。

「プログラミングができないSEは仕事がなくなる」不安を感じたSEが未経験から転職成功するまで
更新日 : 2019年10月7日

書いた人

湯浅 桃太郎

湯浅 桃太郎

20代は役者、30代はエンジニア、そして40代を迎えた現在、Webライターとして活動しながら、3人の息子たちの家庭教育、アンチエイジングなどにも力を入れて日々ノマドライフを楽しんでいます。

おすすめコンテンツ

あなたにぴったりなプログラミング学習プランを無料で診断!

プログラミング学習の効率を劇的に上げる学習メソッドを解説