[インデックス 18933] ファイルの概要
このコミットは、Go言語の標準ライブラリmath/cmplx
パッケージにおけるPow
関数の挙動を修正するものです。具体的には、Pow(0, x)
(0のx乗)の計算において、x
が特定の値を取る場合の振る舞いを、math.Pow
(実数版のべき乗関数)との一貫性を保ちつつ、数学的に正しい定義に近づけることを目的としています。
コミット
commit a9014ba4150b782ee10ab532752f97a7df26846e
Author: Rob Pike <r@golang.org>
Date: Tue Mar 25 11:25:20 2014 +1100
math/cmplx: define Pow(0, x) for problematic values of x.
Currently it's always zero, but that is inconsistent with math.Pow
and also plain wrong.
This is a proposal for how it should be defined.
Fixes #7583.
LGTM=rsc
R=golang-codereviews, iant, gobot, rsc
CC=golang-codereviews
https://golang.org/cl/76940044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/a9014ba4150b782ee10ab532752f97a7df26846e
元コミット内容
このコミットは、math/cmplx
パッケージのPow
関数において、x
が0である場合のべき乗計算の定義を修正するものです。これまでの実装では、Pow(0, x)
は常に0を返していましたが、これは実数におけるmath.Pow
の挙動と矛盾しており、また数学的にも不正確なケースが存在しました。このコミットは、これらの「問題のあるx
の値」に対するPow(0, x)
の振る舞いを、より正確かつ一貫性のあるものにするための提案と実装を含んでいます。
具体的には、以下のケースが考慮されています。
Pow(0, ±0)
は1+0i
を返す。Pow(0, c)
でreal(c) < 0
の場合、imag(c)
がゼロならばInf+0i
を返し、そうでなければInf+Inf i
を返す。
この変更は、Issue #7583を修正するものです。
変更の背景
Go言語の標準ライブラリmath/cmplx
は複素数演算を提供しますが、その中のPow(x, y)
関数(xのy乗)には、x
が0である場合の挙動に不整合がありました。特に、Pow(0, y)
が常に0
を返すという現在の実装は、実数におけるmath.Pow(0, y)
の定義と異なり、また複素数におけるべき乗の数学的定義とも乖離していました。
例えば、実数においては0^0 = 1
と定義されることが多く、また0
の負のべき乗は無限大に発散します。しかし、cmplx.Pow(0, 0)
が0
を返し、cmplx.Pow(0, -1)
も0
を返すという状況は、ユーザーにとって直感的ではなく、バグの原因となる可能性がありました。
このコミットは、このような不整合を解消し、cmplx.Pow
がより堅牢で、数学的に正確な結果を返すようにすることを目的としています。これにより、math.Pow
との一貫性が向上し、複素数演算を利用するプログラムの信頼性が高まります。
前提知識の解説
複素数とべき乗
複素数z = a + bi
(a
は実部、b
は虚部、i
は虚数単位)のべき乗z^w
は、実数のべき乗よりも複雑な定義を持ちます。一般的に、z^w = exp(w * log(z))
として定義されます。ここでexp
は複素指数関数、log
は複素対数関数です。
0
のべき乗の特殊性
実数における0
のべき乗は、いくつかの特殊なケースがあります。
0^0
: これは不定形ですが、文脈によって1
と定義されることが多いです(例: 二項定理、テイラー展開など)。0^x
(x > 0
):0
となります。0^x
(x < 0
):1/0^(-x)
となり、無限大に発散します。
複素数の場合も、これらの実数の概念が拡張されます。特に、0
を底とする複素数のべき乗は、実部と虚部の両方を考慮する必要があります。
IEEE 754浮動小数点標準とNaN/Inf
Go言語のmath
およびcmplx
パッケージは、IEEE 754浮動小数点標準に準拠しています。この標準では、数値計算における特殊な結果を表現するために、NaN
(Not a Number)やInf
(Infinity、無限大)といった特殊な値が定義されています。
math.Inf(1)
: 正の無限大math.Inf(-1)
: 負の無限大complex(math.Inf(1), 0)
: 実部が正の無限大、虚部が0の複素数。Inf()
:cmplx
パッケージで定義されている、実部と虚部が両方とも正の無限大の複素数(complex(math.Inf(1), math.Inf(1))
)。
これらの特殊な値は、数学的な発散や未定義の操作の結果を表現するために使用されます。
技術的詳細
このコミットの主要な変更点は、src/pkg/math/cmplx/pow.go
内のPow
関数に、x
が0である場合の特殊な処理を追加したことです。
変更前のPow
関数は、x
の絶対値(modulus)が0の場合に、単純にcomplex(0, 0)
を返していました。
func Pow(x, y complex128) complex128 {
modulus := Abs(x)
if modulus == 0 {
return complex(0, 0) // This was the problematic line
}
// ...
}
変更後、x == 0
(またはx == -0
)の場合に、y
の実部r
と虚部i
に基づいて、以下のように分岐するロジックが追加されました。
-
r == 0
(yの実部が0の場合):- このケースは、
Pow(0, ±0)
のような状況に対応します。数学的な慣例に従い、1
(1+0i
)を返します。これは実数における0^0 = 1
の定義と一致します。
- このケースは、
-
r < 0
(yの実部が負の場合):- これは
0
の負のべき乗に相当し、結果は無限大に発散します。 i == 0
(yの虚部が0の場合):complex(math.Inf(1), 0)
を返します。これは実部が正の無限大、虚部が0の複素数です。実数における0
の負のべき乗が正の無限大になることと一致します。
i != 0
(yの虚部が0でない場合):Inf()
を返します。Inf()
はcomplex(math.Inf(1), math.Inf(1))
を返すヘルパー関数です。複素数の負のべき乗で虚部が非ゼロの場合、結果は実部と虚部の両方が無限大になることがあります。
- これは
-
r > 0
(yの実部が正の場合):- これは
0
の正のべき乗に相当し、結果は0
となります。
- これは
この新しいロジックにより、cmplx.Pow(0, y)
の挙動が、math.Pow(0, y)
の挙動とより密接に一致し、複素数におけるべき乗の数学的定義に沿ったものになります。
また、src/pkg/math/cmplx/cmath_test.go
には、これらの特殊ケースを検証するための新しいテストケースが追加されています。
// Special cases for Pow(0, c).
var zero = complex(0, 0)
zeroPowers := [][2]complex128{
{0, 1 + 0i}, // Pow(0, 0) should be 1
{1.5, 0 + 0i}, // Pow(0, positive) should be 0
{-1.5, complex(math.Inf(0), 0)}, // Pow(0, negative real) should be Inf+0i
{-1.5 + 1.5i, Inf()}, // Pow(0, negative real + non-zero imag) should be Inf+Inf i
}
for _, zp := range zeroPowers {
if f := Pow(zero, zp[0]); f != zp[1] {
t.Errorf("Pow(%g, %g) = %g, want %g", zero, zp[0], f, zp[1])
}
}
このテストケースは、上記の新しい定義が正しく実装されていることを確認します。
コアとなるコードの変更箇所
src/pkg/math/cmplx/pow.go
--- a/src/pkg/math/cmplx/pow.go
+++ b/src/pkg/math/cmplx/pow.go
@@ -43,7 +43,25 @@ import "math"
// IEEE -10,+10 30000 9.4e-15 1.5e-15
// Pow returns x**y, the base-x exponential of y.
+// For generalized compatiblity with math.Pow:
+// Pow(0, ±0) returns 1+0i
+// Pow(0, c) for real(c)<0 returns Inf+0i if imag(c) is zero, otherwise Inf+Inf i.
func Pow(x, y complex128) complex128 {
+\tif x == 0 { // Guaranteed also true for x == -0.
+\t\tr, i := real(y), imag(y)
+\t\tswitch {\n+\t\tcase r == 0:\n+\t\t\treturn 1\n+\t\tcase r < 0:\n+\t\t\tif i == 0 {\n+\t\t\t\treturn complex(math.Inf(1), 0)\n+\t\t\t}\n+\t\t\treturn Inf()\n+\t\tcase r > 0:\n+\t\t\treturn 0\n+\t\t}\n+\t\tpanic(\"not reached\")
+\t}
modulus := Abs(x)
if modulus == 0 {
return complex(0, 0)
src/pkg/math/cmplx/cmath_test.go
--- a/src/pkg/math/cmplx/cmath_test.go
+++ b/src/pkg/math/cmplx/cmath_test.go
@@ -656,6 +656,19 @@ func TestPolar(t *testing.T) {
}\n }\n func TestPow(t *testing.T) {
+\t// Special cases for Pow(0, c).
+\tvar zero = complex(0, 0)
+\tzeroPowers := [][2]complex128{
+\t\t{0, 1 + 0i},
+\t\t{1.5, 0 + 0i},
+\t\t{-1.5, complex(math.Inf(0), 0)},
+\t\t{-1.5 + 1.5i, Inf()},
+\t}
+\tfor _, zp := range zeroPowers {\n+\t\tif f := Pow(zero, zp[0]); f != zp[1] {\n+\t\t\tt.Errorf("Pow(%g, %g) = %g, want %g", zero, zp[0], f, zp[1])
+\t\t}\n+\t}
\tvar a = complex(3.0, 3.0)\n \tfor i := 0; i < len(vc); i++ {\n \t\tif f := Pow(a, vc[i]); !cSoclose(pow[i], f, 4e-15) {
コアとなるコードの解説
src/pkg/math/cmplx/pow.go
の変更
Pow
関数の冒頭に、x
が0
である場合の新しい条件分岐が追加されました。
if x == 0 { ... }
: この条件は、入力x
が複素数0+0i
である場合に真となります。x == -0
の場合も同様に処理されます。r, i := real(y), imag(y)
:y
の複素数を実部r
と虚部i
に分解します。switch { ... }
:y
の実部r
の値に基づいて、以下の3つのケースに分岐します。case r == 0:
:y
の実部が0
の場合。例えばy = 0+0i
(0
)やy = 0+1i
(純虚数)など。この場合、Pow(0, y)
は1
(1+0i
)を返します。これは0^0 = 1
の慣例を拡張したものです。case r < 0:
:y
の実部が負の場合。例えばy = -1+0i
やy = -2+3i
など。これは0
の負のべき乗に相当し、結果は無限大に発散します。if i == 0
:y
の虚部が0
の場合(例:y = -1+0i
)。実数における0
の負のべき乗と同様に、complex(math.Inf(1), 0)
(正の無限大の実部と0
の虚部)を返します。else
:y
の虚部が0
でない場合(例:y = -1+1i
)。この場合、Inf()
(実部と虚部が両方とも正の無限大)を返します。これは複素数の特性によるものです。
case r > 0:
:y
の実部が正の場合。例えばy = 1+0i
やy = 2-1i
など。この場合、Pow(0, y)
は0
(0+0i
)を返します。これは実数における0
の正のべき乗と同様です。
panic("not reached")
: この行は、上記のswitch
文がすべての可能なケースを網羅しているため、通常は到達しないことを示します。もし到達した場合は、予期せぬ状態であることを示唆します。
この変更により、Pow(0, y)
の挙動がより正確かつ予測可能になり、math.Pow
との一貫性が保たれます。
src/pkg/math/cmplx/cmath_test.go
の変更
TestPow
関数内に、Pow(0, c)
の特殊ケースを検証するための新しいテストブロックが追加されました。
var zero = complex(0, 0)
: 複素数0
を定義します。zeroPowers := [][2]complex128{ ... }
: テストケースの配列を定義します。各要素は[入力y, 期待される出力]
のペアです。{0, 1 + 0i}
:Pow(0, 0)
が1+0i
を返すことをテストします。{1.5, 0 + 0i}
:Pow(0, 1.5)
(正の実部)が0+0i
を返すことをテストします。{-1.5, complex(math.Inf(0), 0)}
:Pow(0, -1.5)
(負の実部、虚部0)がInf+0i
を返すことをテストします。math.Inf(0)
はmath.Inf(1)
と同じく正の無限大を返します。{-1.5 + 1.5i, Inf()}
:Pow(0, -1.5 + 1.5i)
(負の実部、非ゼロ虚部)がInf+Inf i
を返すことをテストします。
for _, zp := range zeroPowers { ... }
: 各テストケースをループで回し、Pow
関数を呼び出して結果を検証します。t.Errorf(...)
: 期待される結果と異なる場合にエラーメッセージを出力します。
これらのテストケースは、pow.go
で実装された新しいロジックが正しく機能していることを保証します。
関連リンク
- Go Issue #7583: https://github.com/golang/go/issues/7583
- Go CL 76940044: https://golang.org/cl/76940044 (Gerrit Code Review)
参考にした情報源リンク
- IEEE 754浮動小数点標準: https://en.wikipedia.org/wiki/IEEE_754
- 複素数のべき乗: https://en.wikipedia.org/wiki/Exponentiation#Complex_exponents
- 0の0乗: https://ja.wikipedia.org/wiki/0%E3%81%AE0%E4%B9%97
- Go言語
math
パッケージドキュメント: https://pkg.go.dev/math - Go言語
math/cmplx
パッケージドキュメント: https://pkg.go.dev/math/cmplx