Python* での苦い経験

インテル® Parallel Studio XEインテル® ディストリビューションの Python*

この記事は、インテル® デベロッパー・ゾーンに公開されている「I Was Just Dipping in a Toe and Got Bit by Python」(https://software.intel.com/en-us/blogs/2016/04/04/i-was-just-dipping-in-a-toe-and-got-bit-by-python) の日本語参考訳です。


私は、ヘルス & ライフ・サイエンス・グループに所属していたときから Python* を少しずつ学んできました。これまでは、Linux* システムにデフォルトでインストールされる Python* 2.6 (Py2) を使用してきましたが、インテル® Distribution for Python* のリリースを機に Python* 3 (Py3) に移行することにしました。

私が取り組んでいたコードの 1 つは、三角形の面積を計算するものでした。三角法を覚えていれば (あるいは Web で検索すれば)、三角形の面積は底辺 × 高さ ÷ 2 であると分かります。私が取り組んでいた問題では、2 等辺三角形を使用する必要がありました。そこで、面積の計算には、等辺のいずれかではなく、底辺を使用することにしました。そうすることで、2 等辺三角形の底辺の半分を 1 辺とし、等辺を斜辺とする直角三角形を形成できるからです。さらに、すべての辺の長さは整数値でなければならないため、ピタゴラスの定理を使用して三角形全体の高さを計算することにしました。そして、直角三角形の 3 辺の関係をテストするため、次の関数を記述ました。

>>> def pythagorean_triple(c, a, b):
...        return (c*c == (a*a + b*b))

直角三角形の底辺と斜辺は分かっているため、上記の式を使用して高さを計算することにしました。斜辺の 2 乗から底辺 (2 等辺三角形の底辺の半分) の 2 乗を引き、差分の平方根を計算し、結果の小数点以下を切り捨てて整数値を得ました。最後の処理は、2 等辺三角形の面積が整数かどうかを確認するためのものです。高さが小数点値の場合、ピタゴラスの定理は成立しないため、2 等辺三角形の面積は整数値になりません。(一般には、整数値の底辺を乗算すれば整数値になるため、これは必ずしも正しいとは限りません。しかし、2 等辺三角形の斜辺の長さには、まだ説明していない特別な関係が存在するため、2 等辺三角形の面積が整数値にならないことは分かっていました。)

>>> e = 126500416/2
>>> e
63250208
>>> d = int(math.sqrt(126500417*126500417 - e*e))
>>> d
109552575
>>> pythagorean_triple(126500417, e, d)
True

これは想定どおりの結果です。Py3 で同じコードを実行したところ、次のようになりました。

>>> e = 126500416/2
>>> e
63250208.0
>>> d = int(math.sqrt(126500417*126500417 - e*e))
>>> d
109552575
>>> pythagorean_triple(126500417, e, d)
False

私はインタラクティブに実行していたので、ピタゴラスの定理をテストする前に、’e’ [行 3] の出力値に困惑しました。偶数 (126500416) を 2 で割っていることが分かっていたので肩をすくめるしかありませんでした。出力値は浮動小数点値ですが、実際の値は算術的に整数値と等しいものです。Py2 の出力とは異なる、そして私が説明してきた三角形とも異なる結果が最後の行に出力されたとき、私は言葉がありませんでした。

最初、私は Python* インタープリターまたはランタイムの不具合を見つけたのかと思いました。しかし、すぐに自分のコードに何らかの問題がある可能性が高いと考え直しました それぞれの実行でコードに問題はありませんでした (実際に、1 つ目の Python* セッションから 2 つ目の Python* セッションにコードをカットアンドペーストしてみました)。次に、私よりも Python* プログラミングに詳しい人達に聞いて回りました。

答えはすぐに分かりました。Py3 では、Py2 から除算演算子の定義が変更されていたのです。具体的には、1 つのスラッシュ (/) 演算子が、オペランドに関係なく浮動小数点値を計算する “true division” (真の除算) に変更されました。Py2 では、オペランドがどちらも整数値の場合、結果の整数部分が商とされました (切り捨て除算)。浮動小数点値の商を得るには、いずれかのオペランドが float (浮動小数点値) である必要がありました。(これは私が慣れ親しんでいる少なくとも 1 つの別のプログラミング言語でも同じで、ほかのプログラミング言語の算術演算に対する考えを改めるものになりました。)

Py3 では 2 つのスラッシュ (//) 演算子を使用すると、整数値の商が得られます。つまり、整数値の辺の長さが必要な場合は、// 演算子を使用すべきです。’e’ の出力値が浮動小数点値であったときに、このことに気付くべきでした。この経験は、アプリケーションで想定外の動作に驚く前に、使用するツールについてよく理解することの重要性を認識する良い機会となりました。問題が生じる可能性のあるすべての詳細を知る必要はありませんが、今回の Python* バージョン間の変更のように明白なもの (そして、オンラインで詳しくドキュメント化されているもの) は把握しておくべきです。今後このようなことがないように、Python* 3 のテキストをもっとよく読んでおこうと思います。

コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。

タイトルとURLをコピーしました