【Python】Decimal 型数値の quantize における str 関数の使用上の注意
Abstract
Python の Decimal 型数値を丸める quantize において、quantize のケタ数を指示するための Decimal 型数値の作成に str 関数を使用すると、期待した丸めにならない場合がある。それは、1, 10, …… の位で丸めたい場合である。
str 関数を使わず、クォーテーションで文字列とするのが安全。
また、ある変数を参照して文字列化したものを quantize のケタ数指示に用いる場合は、文字列化の際に、format で有効数字の指定を行うとよい。
Result
例えば、小数第1, 2, ……位で丸めたい場合、以下のように、str 関数を用いても、クォーテーションを用いても、同じ結果が得られる。
>>> decimal.Decimal('562.79').quantize(decimal.Decimal(str(1E-1)), rounding='ROUND_UP') Decimal('562.8') >>> decimal.Decimal('562.79').quantize(decimal.Decimal('1E-1'), rounding='ROUND_UP') Decimal('562.8')
しかし、1, 10, ……の位で丸めたい場合、str 関数を用いると所望の結果は得られず、クォーテーションを用いると所望の結果が得られる。
>>> decimal.Decimal('562.79').quantize(decimal.Decimal(str(1E0)), rounding='ROUND_UP') Decimal('562.8') >>> decimal.Decimal('562.79').quantize(decimal.Decimal('1E0'), rounding='ROUND_UP') Decimal('563')
>>> decimal.Decimal('562.79').quantize(decimal.Decimal(str(1E1)), rounding='ROUND_UP') Decimal('562.8') >>> decimal.Decimal('562.79').quantize(decimal.Decimal('1E1'), rounding='ROUND_UP') Decimal('570')
これは、str 関数を用いたときと、クォーテーションを用いたときとで、内部での扱われ方が異なるため。
DecimalTuple(sign, digittuple, exponent) を比較すると、以下のようになっている。
>>> decimal.Decimal(str(1E1)).as_tuple() DecimalTuple(sign=0, digits=(1, 0, 0), exponent=-1) >>> decimal.Decimal('1E1').as_tuple() DecimalTuple(sign=0, digits=(1,), exponent=1)
>>> decimal.Decimal(str(1E0)).as_tuple() DecimalTuple(sign=0, digits=(1, 0), exponent=-1) >>> decimal.Decimal('1E0').as_tuple() DecimalTuple(sign=0, digits=(1,), exponent=0)
なお、1E-1以下では、どちらでも同じ扱われ方になっている。
>>> decimal.Decimal(str(1E-1)).as_tuple() DecimalTuple(sign=0, digits=(1,), exponent=-1) >>> decimal.Decimal('1E-1').as_tuple() DecimalTuple(sign=0, digits=(1,), exponent=-1)
変数を受け取って、その変数に基づき quantize のケタを指示したい場合は、format において有効数字を 1 ケタにするとよい。そうすると、所望の結果が得られる。
>>> a = 1E2 >>> decimal.Decimal('562.79').quantize(decimal.Decimal('{:.1g}'.format(a)), rounding = 'ROUND_UP') Decimal('6E+2') >>> b = 1E1 >>> decimal.Decimal('562.79').quantize(decimal.Decimal('{:.1g}'.format(b)), rounding = 'ROUND_UP') Decimal('5.7E+2') >>> c = 1E0 >>> decimal.Decimal('562.79').quantize(decimal.Decimal('{:.1g}'.format(c)), rounding = 'ROUND_UP') Decimal('563') >>> d = 1E-1 >>> decimal.Decimal('562.79').quantize(decimal.Decimal('{:.1g}'.format(d)), rounding = 'ROUND_UP') Decimal('562.8')