他の回答のような浮動小数点エラーなしで、常に入力とまったく同じ精度で出力を返す次のメソッドを作成しました。
def percent_to_float(s):
s = str(float(s.rstrip("%")))
i = s.find(".")
if i == -1:
return int(s) / 100
if s.startswith("-"):
return -percent_to_float(s.lstrip("-"))
s = s.replace(".", "")
i -= 2
if i < 0:
return float("." + "0" * abs(i) + s)
else:
return float(s[:i] + "." + s[i:])
説明
- 末尾の「%」を削除します。
- パーセントに「.」がない場合は、単純に 100 で割った値を返します。
- パーセントが負の場合は、「-」を削除して関数を再呼び出しし、結果を負に変換して返します。
- 小数点以下を削除します。
- 小数点
i
以下を 2 スペース左にシフトしたいので、(小数点以下の位置のインデックス) を 2 減らします。
- が負の場合
i
、ゼロでパディングする必要があります。
- 例:入力が「1.33%」であるとします。小数点以下を 2 スペース左にシフトできるようにするには、ゼロを埋め込む必要があります。
- float に変換します。
テストケース (オンラインで試す):
from unittest.case import TestCase
class ParsePercentCase(TestCase):
tests = {
"150%" : 1.5,
"100%" : 1,
"99%" : 0.99,
"99.999%" : 0.99999,
"99.5%" : 0.995,
"95%" : 0.95,
"90%" : 0.9,
"50%" : 0.5,
"66.666%" : 0.66666,
"42%" : 0.42,
"20.5%" : 0.205,
"20%" : 0.2,
"10%" : 0.1,
"3.141592653589793%": 0.03141592653589793,
"1%" : 0.01,
"0.1%" : 0.001,
"0.01%" : 0.0001,
"0%" : 0,
}
tests = sorted(tests.items(), key=lambda x: -x[1])
def test_parse_percent(self):
for percent_str, expected in self.tests:
parsed = percent_to_float(percent_str)
self.assertEqual(expected, parsed, percent_str)
def test_parse_percent_negative(self):
negative_tests = [("-" + s, -f) for s, f in self.tests]
for percent_str, expected in negative_tests:
parsed = percent_to_float(percent_str)
self.assertEqual(expected, parsed, percent_str)