浮動小数点数の改善点
却下されたPEP-754では、PythonのIEEE 754完全準拠が提案されていました。これが却下されたのは、NaN(非数)や正負の無限大など、IEEE 754特殊値を処理するのに、Python(CPython実装)がCライブラリに依存しているためです。異なるプラットフォーム間に多くの不整合があります。それでも、Python 3.0では(2.6でも)浮動小数点数に多くの改良が加えられ、IEEE 754標準の実装も大幅に進みました。
文字列を浮動小数点数に変換するfloat()関数は、nan、+inf(またはinf)、および-infを認識し、IEEE 754の非数、正の無限大、負の無限大に変換するようになりました(大文字小文字は区別されず、NaN、INFなども有効です)。
mathモジュールに、isnan()関数とisinf()関数が追加されました。isinf()関数では、inf、+inf、-infは区別されません。以下に使用例を示します。
>>> float('nan')
nan
>>> float('NaN') # Any case works
nan
>>> float('+inf')
inf
>>> float('-inf')
-inf
>>> float('INF')
inf
>>> float('nan') + float('inf')
nan
>>> float('inf') + float('-inf')
nan
>>> float('inf') - float('-inf')
inf
>>> import math
>>> math.isnan(float('nan'))
True
>>> math.isinf(float('inf'))
True
>>> math.isinf(float('-inf'))
True
>>> math.isinf(float('nan'))
False
>>> math.isnan(float('-inf'))
False
mathモジュールには、copysign(x, y)関数も追加されました。この関数は、xの絶対値に符号yを付けて返します。-1と1を返す単純なsign()や、ispositive()、isnegative()といった関数ではなく、どうしてこの関数なのか、よく分かりません。ドキュメントにもごく簡潔な記述しかありません。
>>> help(math.copysign)
Help on built-in function copysign in module math:
copysign(...)
copysign(x,y)
Return x with the sign of y.
とはいえ、copysign()関数はこの説明のとおりに動作します。ただし、NaNの場合は別です。NaNの符号をコピーしようとした場合の結果は一貫せず、Mac OS Xでは負の符号、Windowsでは正の符号になります。クローズ済みのバグによると、この動作で問題なし、とされていますが、同意できません。NaNは非数なので、符号はありません。NaNから符号をコピーしようとすることは、数値ではないそれ以外の値(文字列、リスト、オブジェクト)から符号をコピーしようとするのと同じであり、例外を発生させるべきです。
mathモジュールには、浮動小数点数関連の関数がこれ以外にも追加されています。math.fsum()は、繰り返し可能オブジェクト(iterable)から数値のストリームの合計を出す関数で、sum()組み込み関数とは異なり、部分合計による精度ロスを入念に回避しています。いずれかの数値がNaNの場合、結果はNaNになります。部分合計が+infまたは-infに達すると、sum()関数はそれを結果として返しますが、math.fsum()関数はOverflowError例外を発生させます。これは、よりIEEE 754に沿った動作です。
>>> sum([1e308, 1, -1e308])
0.0
>>> math.fsum([1e308, 1, -1e308])
1.0
>>> sum([1e100, 1, -1e100, -1])
-1.0
>>> math.fsum([1e100, 1, -1e100, -1])
0.0
>>> x = [1e308, 1e308, -1e308]
>>> sum(x)
inf
>>> math.fsum(x)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: intermediate overflow in fsum
>>> sum([float('nan'), 3.3])
nan
>>> math.fsum([float('nan'), -float('nan')])
nan
acosh()関数、asinh()関数、atanh()関数は、逆双曲線関数を計算します。log1p()関数は1+x (base e)の自然対数を返します。trunc()関数は、数値の小数点以下を切り捨てて近似値の整数値を返します。
>>> math.acosh(30) 4.0940666686320855 >>> math.acosh(1) 0.0 >>> math.asinh(1) 0.88137358701954305 >>> math.asinh(0) 0.0 >>> math.atanh(0.5) 0.54930614433405489 >>> math.log1p(2) 1.0986122886681098 >>> math.trunc(-1.1) -1 >>> math.trunc(-1.9) -1 >>> math.trunc(1.1) 1 >>> math.trunc(1.9) 1 >>> math.trunc(3.0) 3
浮動小数点数と16進文字列の間の変換を行うことができます。変換関数は、浮動小数点数を文字列表現へ、文字列表現を浮動小数点数へ変換し、10進と2進の変換の丸め誤差を発生させません(その数値を完全に表現する桁数がある場合)。floatのhex()メソッドは文字列を返し、float.fromhex()メソッドは文字列を(可能な限り正確に)数値に変換します。
>>> x = 4.2
>>> a.hex()
'0x1.0cccccccccccdp+2'
>>> float.fromhex('0x1.0cccccccccccdp+2')
4.2000000000000002
decimalモジュールは汎用10進演算仕様バージョン1.66に更新されました。新しい機能として、exp()やlog10()などの基本的な算術関数のメソッドが追加されました。
>>> Decimal(1).exp()
Decimal("2.718281828459045235360287471")
>>> Decimal("2.7182818").ln()
Decimal("0.9999999895305022877376682436")
>>> Decimal(1000).log10()
Decimal("3")
Decimalオブジェクトのas_tuple()メソッドは、符号、仮数、指数のフィールドを持つ名前付きタプルを返すようになりました(名前付きタプルについては今後の記事で説明します)。
>>> Decimal('-3.3').as_tuple()
DecimalTuple(sign=1, digits=(3, 3), exponent=-1)
sysモジュールの新しい変数float_infoは、float.hファイルから得たプラットフォームの浮動小数点数サポート情報が含まれるオブジェクトです。
>>> sys.float_info sys.floatinfo(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.2204460492503131e-16, radix=2, rounds=1)
このように、Pythonの浮動小数点数サポートは確かに向上しましたが、まだ完璧ではありません。Pythonで重要な数値演算を行うには、Numpy外部パッケージが依然として最善の策と言えます。
