見出し画像

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としても問題なく計算できた。
多分、この辺りが上述のエラーの原因かと思われます。

ふー、スッキリ!



ここまで読んでいただき、ありがとうございました。

この記事が参加している募集

やってみた

これまでの収益は全て、それを必要としておられる方々へ、支援機関を通して寄付させていただきました。この活動は今後も継続したいと思っています。引き続きよろしくお願いいたします。