Pythonで小数点以下の数値を切り捨てたいときを考えます。例えば、10.987654321と入力した場合に、小数点2位まで取り出して、10.98と返してくれるような関数を考えます。
環境
- Windows 10
- python=3.7.3
方法① (精度に問題あり)
値を100倍して、Intにして、100で割る
def round_off_to_two_decimal_places_1(input_val: float) -> float:
output = int(input_val * 100) / 100
return output
def round_off_test(round_off_method):
input_val = 10.987654321
print(input_val, "->", round_off_method(input_val))
input_val = 10.12
print(input_val, "->", round_off_method(input_val))
input_val = 10.99
print(input_val, "->", round_off_method(input_val))
input_val = 10.74297492
print(input_val, "->", round_off_method(input_val))
input_val = 10.155
print(input_val, "->", round_off_method(input_val))
input_val = 10.585018
print(input_val, "->", round_off_method(input_val))
input_val = 10.000000
print(input_val, "->", round_off_method(input_val))
input_val = 10.
print(input_val, "->", round_off_method(input_val))
print("")
round_off_test(round_off_to_two_decimal_places_1)
実行結果
10.987654321 -> 10.98
10.12 -> 10.11
10.99 -> 10.99
10.74297492 -> 10.74
10.155 -> 10.15
10.585018 -> 10.58
10.0 -> 10.0
10.0 -> 10.0
良さそうに見えますが、10.12を入力した場合に10.11と返されてしまっています。
方法②
文字列として操作する。
def round_off_to_two_decimal_places_2(input_val: float) -> float:
str_input_val = str(input_val)
split_str = str_input_val.split('.')
if len(split_str[1]) > 2:
split_str[1] = split_str[1][:2] # 小数点以下2桁取り出す
str_output = '.'.join(split_str)
return float(str_output)
round_off_test(round_off_to_two_decimal_places_2)
実行結果
10.987654321 -> 10.98
10.12 -> 10.12
10.99 -> 10.99
10.74297492 -> 10.74
10.155 -> 10.15
10.585018 -> 10.58
10.0 -> 10.0
10.0 -> 10.0
力技ですが、一応意図した値は返してくれるようです。
方法③
Decimalのquantizeメソッドを使う。
from decimal import Decimal, ROUND_DOWN
def round_off_to_two_decimal_places_3(input_val: float) -> float:
TWOPLACES = Decimal(10) ** -2
output = Decimal(str(input_val)).quantize(TWOPLACES, rounding=ROUND_DOWN)
return float(output)
round_off_test(round_off_to_two_decimal_places_3)
実行結果
10.987654321 -> 10.98
10.12 -> 10.12
10.99 -> 10.99
10.74297492 -> 10.74
10.155 -> 10.15
10.585018 -> 10.58
10.0 -> 10.0
10.0 -> 10.0
意図した値が返ってきてます。
補足
ContextのtrapsにInexactを設定すると、丸めが発生したことを通知することもできます。
from decimal import Decimal, ROUND_DOWN, Context, Inexact
def round_off_to_two_decimal_places_4(input_val: float) -> float:
TWOPLACES = Decimal(10) ** -2
try:
output = Decimal(str(input_val)).quantize(
TWOPLACES, rounding=ROUND_DOWN, context=Context(traps=[Inexact]))
except Inexact:
print("値の丸めが発生")
output = Decimal(str(input_val)).quantize(TWOPLACES,
rounding=ROUND_DOWN)
return float(output)
round_off_test(round_off_to_two_decimal_places_4)
実行結果
値の丸めが発生
10.987654321 -> 10.98
10.12 -> 10.12
10.99 -> 10.99
値の丸めが発生
10.74297492 -> 10.74
値の丸めが発生
10.155 -> 10.15
値の丸めが発生
10.585018 -> 10.58
10.0 -> 10.0
10.0 -> 10.0
結果
Pythonで浮動小数点を扱う場合は、Decimalを使うのがよさそうです。