Pythonで、平方根を計算してみた
Pythonで遊び始めて二日目。
√5の計算で、TypeErrorとなる謎を検証中。
昨日時点のソースコードはこちら。(Ayumiさんのロジックを借用)
def root_digit(square, first, step):
r = first
for i in range(1, 10, 1):
if square < ((r + step) * (r + step)):
return r
r += step
def root(square):
r = 0
step = 1
for i in range(1, 10, 1):
r = root_digit(square, r, step)
step = step / 10
print('{:.15f}'.format(r))
return r
root2 = root(5)
print('{:.15f}'.format(root2))
実行結果
jm3nrhMac-mini-:python_lesson akio$ python square_root_5.py
2.000000000000000
2.200000000000000
2.230000000000000
2.235999999999999
2.235999999999999
2.236059999999999
2.236067000000000
Traceback (most recent call last):
File "/Users/akio/Desktop/python_lesson/square_root_5.py", line 17, in <module>
root2 = root(5)
^^^^^^^
File "/Users/akio/Desktop/python_lesson/square_root_5.py", line 14, in root
print('{:.15f}'.format(r))
^^^^^^^^^^^^^^^^^^^
TypeError: unsupported format string passed to NoneType.__format__
jm3nrhMac-mini-:python_lesson akio$
考察
1から増分1で順番に(1、2、3、、、)二乗して、√を求めたい値を超える直前の値を求め、次は増分を1/10した値(1.1、1.2、1.3、、、)を二乗して、√を求めたい値を超える直前の値を求め、、、と言う具合に、足し算と掛け算をしているだけなのに、計算した結果が数値ではなくNoneという状態になっていた。
√2から√8までを計算したところ、√5と√6でこの現象が出現。
現時点では理由はわからない。
とりあえずこの問題を回避(対症療法)したソースコードがこちら
def root_digit(square, first, step):
r = first
for i in range(1, 10, 1):
if square < ((r + step) * (r + step)):
return r
r = r + step
def root(square):
r = 0.0
step = 1.0
for i in range(1, 15, 1):
t = root_digit(square, r, step)
if not t is None:
r = t
step = step / 10.0
return r
for i in range(2,10,1):
root2 = root(i)
print('Root({0}) = {1:.15f}'.format(i, root2))
実行結果
Root(2) = 1.414213562373000
Root(3) = 1.732050807568802
Root(4) = 2.000000000000000
Root(5) = 2.236067000000000
Root(6) = 2.440000000000000
Root(7) = 2.645751311064501
Root(8) = 2.828427124746102
Root(9) = 3.000000000000000
jm3nrhMac-mini-:python_lesson akio$
CASIOの電卓での計算結果と比較すると、
√2、√3、√4、√7、√8、√9は、小数点以下9桁まで一致
√5は、小数点以下6桁まで一致
√6は、小数点以下2桁まで一致
という具合に、√5と√6の精度が極端に低くなっている。
(まぁこれは、計算結果がNoneとなってしまった時点で計算を打ち切っているので、当然と言えば当然。)
結論
小数点以下の数字の足し算の結果がNoneとなってしまう理由は、よくわかりません。
処理系が潜在的に持つ弱点?
二進数で、小数点以下の計算をした場合に必然的に発生する問題?
余裕ができたら、他の言語でも試してみよっと。
追記 - 1
Pythonの実行環境の不具合も未だ未解決。
しばらく頭を悩ませそうだ、、、。
今日も良い1日を!
追記 - 2 Ayumiさんからのアドバイスで問題解決!
def root_digit(square, first, step):
r = first
for i in range(1, 20, 1):
if square < ((r + step) ** 2):
return r
r += step
def root(square):
r = 0.0
step = 1.0
for i in range(1, 15, 1):
t = root_digit(square, r, step)
if not t is None:
r = t
step /= 10.0
return r
for i in range(2,10,1):
root2 = root(i)
print('Root({0}) = {1:.15f}'.format(i, root2))
root_digitがLoopする回数を20にしたら、解決!
Root(2) = 1.414213562373000
Root(3) = 1.732050807568802
Root(4) = 2.000000000000000
Root(5) = 2.236067977499699
Root(6) = 2.449489742783100
Root(7) = 2.645751311064501
Root(8) = 2.828427124746102
Root(9) = 3.000000000000000
jm3nrhMac-mini-:python_lesson akio$
計算結果もあっているみたい。
計算途中結果をダンプして眺めてみていたのですが、2進数で小数点以下を表現することによる誤差のため、本来0であるべき桁に9が入っていたりして、そのため各桁で最大10回テストしないといけないのに最大9回しかできていなかったことが判明。このため、root_digitがLoopする回数を20とすることでうまく収束したみたい。回数を11としても問題なく計算できた。
多分、この辺りが上述のエラーの原因かと思われます。
ふー、スッキリ!
ここまで読んでいただき、ありがとうございました。