4
3

More than 3 years have passed since last update.

JavaのBigDecimalの割り算の丸め処理をPythonに書き換える

Last updated at Posted at 2021-04-27

最近Javaで書かれたバッチをPythonに書き換えている。
Javaで正確な丸めを行うためにBigDecimalを使ったコードがある。
それをPythonに書き換える際に対応するメソッドがなく困った。
本記事では対応策をまとめる。

Java側

Javaのコードが以下のようだとする。

import java.math.BigDecimal;
import java.math.RoundingMode;

class BigDecimalDivideTest {
        public static void main(String[] args) {
                BigDecimal someValue = BigDecimal.valueOf(3215);
                BigDecimal BD_100 = BigDecimal.valueOf(1000);
                int SCALE_2 = 2;

                System.out.println(someValue.divide(BD_100, SCALE_2, RoundingMode.HALF_UP));
                // 3.22が表示される
        }
  }

Python側

from decimal import *

some_value = Decimal('3215')
bd_100 = Decimal('1000')
scale_2 = 2

divided_value = some_value / bd_100
scaled_decimal = Decimal(str(10 ** - scale_2)) # => Decimal('0.01')になる。

# 丸め処理にはquantizeメソッドを使う
divided_value.quantize(scaled_decimal, rounding=ROUND_HALF_UP)
# => Decimal('3.22')

説明

Pythonにおける、JavaのBigDecimalに対応するライブラリはdecimalである。

だが decimaldivide メソッドでは、Javaのようにスケールや丸めについて引数を取るものがない。
なので、スケール処理と丸めはquantizeで割り算のあとに行う。

なお、quantizeでエラーが出る場合がある。

from decimal import *

getcontext().prec = 2 # 追加
some_value = Decimal('3215')
bd_100 = Decimal('1000')
scale_2 = 2

divided_value = some_value / bd_100
scaled_decimal = Decimal(str(10 ** - scale_2))

divided_value.quantize(scaled_decimal, rounding=ROUND_HALF_UP)
# => decimal.InvalidOperation 例外が発生する

他の操作と違い、打ち切り(quantize)操作後の係数の長さが精度を越えた場合には、 InvalidOperation がシグナルされます。

getcontext().prec = 2を追加した上でquantizeを行うと、quantize結果が数値全体で2桁(3.2とか)でないとエラーになる。
今回はquantize結果は3.22であり結果が3桁になるので、InvalidOperationが出る。


なんで書こうと思ったか

Pythonのドキュメントの一番上で以下のようなコードが書いてある。

from decimal import *

getcontext().prec = 6
Decimal(1) / Decimal(7)
# => Decimal('0.142857')

これを見て、私は桁数を指定するにはgetcontext().precを指定すればいいのかとミスリードした。
precは数値全体の桁数の話なので、prec = 6としても小数点以下6桁を保証してくれるわけではない。

例えば上記の割り算の対象数字が大きかったとすると、以下のようになる。

from decimal import *

getcontext().prec = 6
print(Decimal(10000000) / Decimal(7))
# => Decimal('1.42857E+6')

print(float(Decimal(10000000) / Decimal(7)))
# floatに変換すると1428570.0
# prec = 有効桁数は6なので、142857までの6桁までは計算結果が保持されるがそれ以降は0に

参考

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3