近年、多くのプログラミング言語で「複素数 (complex number)」がサポートされるようになりました。たとえば、C言語では 1999 年に発行された規格 C99 で複素数型が導入されました。Go 言語や Python でも複素数型をサポートしていますし、複素数用の標準ライブラリを用意している言語 (C++, Ruby, Haskell など) も多くあります。他の言語では、FORTRAN や Common Lisp が昔から複素数型をサポートしています。今回は Go 言語の複素数についてまとめてみました。
Go 言語は標準で整数、浮動小数点数、複素数を使うことができます。下表に数値の種類を示します。
型名 | 範囲 |
---|---|
int8 | -128 ~ 127 (8 bit) |
int16 | -32768 ~ 32767 (16 bit) |
int32 | -2147483648 ~ 2147483647 (32 bit) |
int64 | -9223372036854775808 ~ 9223372036854775807 (64 bit) |
int | 32 bit 処理系ならば int32, 64 bit 処理系ならば int64 |
uint8 | 0 ~ 255 (8 bit) |
byte | uint8 の別名 |
uint16 | 0 ~ 65535 (16 bit) |
uint32 | 0 ~ 4294967295 (32 bit) |
uint64 | 0 ~ 18446744073709551615 (64 bit) |
uint | 32 bit 処理系ならば uint32, 64 bit 処理系ならば uint64 |
float32 | ±1.1754944E-38 ~ 3.4028235E+38 (32 bit 単精度) |
float64 | ±2.22507E-308 ~ 1.79769E+308 (64 bit 倍精度) |
complex64 | float32 の実数部と虚数部を持つ複素数 |
complex128 | float64 の実数部と虚数部を持つ複素数 |
浮動小数点数には IEEE 754 という標準仕様があり、近代的なプログラミング言語のほとんどは、IEEE 754 に準拠した浮動小数点数をサポートしています。浮動小数点数はすべての小数を正確に表現することはできないので、その値は近似的 (不正確) なものとなります。IEEE 754 には通常の数値以外にも、負のゼロ (-0.0)、正負の無限大 (∞, -∞)、NaN (Not a Number, 非数) といった値が定義されています。これらの値は Go 言語でも取り扱うことができます。
一般に、無限大は値のオーバーフロー、ゼロ除算 (数値 / 0.0)、数学関数の計算結果 (たとえば log(0.0)) などで発生します。Go 言語の標準ライブラリ math には正負の無限大を生成する関数 Inf() と無限大を判定する関数 IsInf() が用意されています。
func Inf(sign int) float64 func IsInf(f float64, sign int) bool
関数 Inf() は引数 sign が 0 以上ならば正の無限大を、負ならば負の無限大を返します。関数 IsInf() は引数 f が無限大か判定します。このとき、引数 sign が正ならば正の無限大、負ならば負の無限大、0 ならば正負の無限大を判定します。なお、Print() や Printf() などの出力関数は正負の無限大を +Inf, -Inf と表示します。
簡単な使用例を示しましょう。
リスト : 無限大の使用例 (inf1.go) package main import ( "fmt" "math" ) func main() { a := math.Inf(1) b := math.Inf(-1) c := math.MaxFloat64 d := -math.MaxFloat64 z := 0.0 fmt.Printf("a = %g\n", a) fmt.Printf("b = %g\n", b) fmt.Printf("c = %g\n", c) fmt.Printf("d = %g\n", d) fmt.Printf("IsInf(a, 1) = %t\n", math.IsInf(a, 1)) fmt.Printf("IsInf(a, 0) = %t\n", math.IsInf(a, 0)) fmt.Printf("IsInf(a, -1) = %t\n", math.IsInf(a, -1)) fmt.Printf("IsInf(b, 1) = %t\n", math.IsInf(b, 1)) fmt.Printf("IsInf(b, 0) = %t\n", math.IsInf(b, 0)) fmt.Printf("IsInf(b, -1) = %t\n", math.IsInf(b, -1)) fmt.Printf("IsInf(c, 1) = %t\n", math.IsInf(c, 1)) fmt.Printf("IsInf(c, 0) = %t\n", math.IsInf(c, 0)) fmt.Printf("IsInf(c, -1) = %t\n", math.IsInf(c, -1)) fmt.Printf("c * 2 = %g\n", c * 2.0) fmt.Printf("d * 2 = %g\n", d * 2.0) // fmt.Printf("1.0 / 0.0 = %g\n", 1.0 / 0.0) コンパイルエラー (division by zero) fmt.Printf("1.0 / 0.0 = %g\n", 1.0 / z) fmt.Printf("log(0.0) = %g\n", math.Log(0.0)) }
$ go run inf1.go a = +Inf b = -Inf c = 1.7976931348623157e+308 d = -1.7976931348623157e+308 IsInf(a, 1) = true IsInf(a, 0) = true IsInf(a, -1) = false IsInf(b, 1) = false IsInf(b, 0) = true IsInf(b, -1) = true IsInf(c, 1) = false IsInf(c, 0) = false IsInf(c, -1) = false c * 2 = +Inf d * 2 = -Inf 1.0 / 0.0 = +Inf log(0.0) = -Inf
無限大は他の数値と比較したり演算することもできますが、結果が非数 (NaN, 出力関数では NaN と表示) になることもあります。
リスト : 無限大の使用例 (inf2.go) package main import ( "fmt" "math" ) func main() { a := math.Inf(1) b := math.Inf(-1) fmt.Printf("a = %g\n", a) fmt.Printf("b = %g\n", b) fmt.Printf("a == a => %t\n", a == a) fmt.Printf("a == b => %t\n", a == b) fmt.Printf("a != a => %t\n", a != a) fmt.Printf("a != b => %t\n", a != b) fmt.Printf("a > b => %t\n", a > b) fmt.Printf("a < b => %t\n", a < b) fmt.Printf("a > 0.0 => %t\n", a > 0.0) fmt.Printf("a < 0.0 => %t\n", a < 0.0) fmt.Printf("b < 0.0 => %t\n", b < 0.0) fmt.Printf("a + 10.0 = %g\n", a + 10.0) fmt.Printf("a - 10.0 = %g\n", a - 10.0) fmt.Printf("a * 10.0 = %g\n", a * 10.0) fmt.Printf("a / 10.0 = %g\n", a / 10.0) fmt.Printf("a + a = %g\n", a + a) fmt.Printf("a * a = %g\n", a * a) fmt.Printf("a - a = %g\n", a - a) fmt.Printf("a / a = %g\n", a / a) fmt.Printf("a + b = %g\n", a + b) fmt.Printf("a * 0.0 = %g\n", a * 0.0) }
$ go run inf2.go a = +Inf b = -Inf a == a => true a == b => false a != a => false a != b => true a > b => true a < b => false a > 0.0 => true a < 0.0 => false b < 0.0 => true a + 10.0 = +Inf a - 10.0 = +Inf a * 10.0 = +Inf a / 10.0 = +Inf a + a = +Inf a * a = +Inf a - a = NaN a / a = NaN a + b = NaN a * 0.0 = NaN
負のゼロ (-0.0) は、計算結果が負の極めて小さな値でアンダーフローになったとき発生します。また、正の値を負の無限大で除算する、負の値を正の無限大で除算する、負の値と 0.0 を乗算しても -0.0 が得られます。出力関数では負のゼロを -0 と表示します。
なお、Go 言語はプログラムで -0.0 と書いても負のゼロにはなりません。標準ライブラリ math の関数 Copysign() を使うと、負のゼロを得ることができます。また、関数 Signbit() を使って負数を判定することができます。
func Copysign(x, y float64) float64 func Signbit(x float64) bool
Copysign() は引数 x と同じ大きさで引数 y と同じ符号の数値を返します。Signbit() は引数 x が負数または負のゼロであれば真を返します。IEEE 754 では、演算子 (Go 言語では ==) による 0.0 と -0.0 の比較は等しいと判定されます。負のゼロの判定は Signbit() を使ってください。
リスト : 負のゼロ (nzero1.go) package main import ( "fmt" "math" ) func main() { a := -1e-323 b := math.Inf(1) c := math.Inf(-1) z1 := 0.0 z2 := math.Copysign(0, -1) fmt.Printf("a = %g\n", a) fmt.Printf("z1 = %g\n", z1) fmt.Printf("z2 = %g\n", z2) fmt.Printf("a / 2 = %g\n", a / 2.0) fmt.Printf("a / 4 = %g\n", a / 4.0) fmt.Printf("1.0 / -inf = %g\n", 1.0 / c) fmt.Printf("-1.0 / inf = %g\n", -1.0 / b) fmt.Printf("-1.0 * 0.0 = %g\n", -1.0 * z1) fmt.Printf("z1 == z2 => %t\n", z1 == z2) fmt.Printf("z1 != z2 => %t\n", z1 != z2) fmt.Printf("Signbit(z1) => %t\n", math.Signbit(z1)) fmt.Printf("Signbit(z2) => %t\n", math.Signbit(z2)) }
$ go run nzero1.go a = -1e-323 z1 = 0 z2 = -0 a / 2 = -5e-324 a / 4 = -0 1.0 / -inf = -0 -1.0 / inf = -0 -1.0 * 0.0 = -0 z1 == z2 => true z1 != z2 => false Signbit(z1) => false Signbit(z2) => true
なお、-0.0 は数学関数 (math.h の関数 atan2 など) や複素数の演算処理などで使われます。
リスト : 負のゼロ (nzero2.go) package main import ( "fmt" "math" ) func main() { z1 := 0.0 z2 := math.Copysign(0, -1) fmt.Printf("sqrt(0.0) => %g\n", math.Sqrt(z1)) fmt.Printf("sqrt(-0.0) => %g\n", math.Sqrt(z2)) fmt.Printf("atan2(0.0, -1.0) => %g\n", math.Atan2(z1, -1.0)) fmt.Printf("atan2(-0.0, -1.0) => %g\n", math.Atan2(z2, -1.0)) }
$ go run nzero2.go sqrt(0.0) => 0 sqrt(-0.0) => -0 atan2(0.0, -1.0) => 3.141592653589793 atan2(-0.0, -1.0) => -3.141592653589793
NaN は数ではないことを表す特別な値 (非数) です。一般的には 0.0 / 0.0 といった不正な演算を行うと、その結果は NaN になります。標準ライブラリ math には NaN を返す関数 NaN() と NaN を判定する関数 isNaN() が用意されています。
func NaN() float64 func IsNaN(f float64) bool
リスト : 非数 (nantest.go) package main import ( "fmt" "math" ) func main() { a := math.NaN() b := math.Inf(1) fmt.Printf("NaN = %g\n", a) fmt.Printf("NaN / NaN = %g\n", a / a) fmt.Printf("inf / inf = %g\n", b / b) fmt.Printf("IsNaN(NaN) = %t\n", math.IsNaN(a)) fmt.Printf("IsNaN(inf) = %t\n", math.IsNaN(b)) }
$ go run nantest.go NaN = NaN NaN / NaN = NaN inf / inf = NaN IsNaN(NaN) = true IsNaN(inf) = false
数学では複素数 \(z\) を \(x + yi\) と表記します。x を実部、y を虚部、i を虚数単位といいます。虚数単位は 2 乗すると -1 になる数です。実部と虚部の 2 つの数値を格納するデータ構造を用意すれば、プログラミング言語でも複素数を表すことができます。Go 言語で複素数を扱うときは標準ライブラリ math/cmplx をインポートしてください。cmplx には複素数の計算に必要な定義や関数が含まれています。
複素数のデータ型は complex64 と complex128 の二種類があります。実部と虚部の数値が float32 だと complex64 になり、float64 だと complex128 になります。本稿では complex128 を使用することにします。
Go 言語は複素数 z = x + yi を x + yi と表記します。また、関数 complex() で複素数を生成することもできます。
func complex(r, i FloatType) ComplexType func real(c ComplexType) FloatType func imag(c ComplexType) FloatType
FloatType は実部と虚部の数値型を表します。たとえば、FloatType が float64 ならば CompelxType は complex128 になります。実部は関数 real() で、虚部は関数 imag() で求めることができます。複素数の虚部の符号を反転することを「複素共役」といいます。複素共役は cmplx の関数 Conj() で求めることができます。
簡単な例を示しましょう。
リスト : 複素数の簡単な使用例 (ctest1.go) package main import ( "fmt" "math/cmplx" ) func main() { a := 1.0 + 2.0i b := 3.0 - 4.0i c := complex(-5.0, -6.0) fmt.Printf("a = %g\n", a) fmt.Printf("b = %g\n", b) fmt.Printf("c = %g\n", c) fmt.Printf("real(a) = %g\n", real(a)) fmt.Printf("imag(a) = %g\n", imag(a)) fmt.Printf("real(b) = %g\n", real(b)) fmt.Printf("imag(b) = %g\n", imag(b)) fmt.Printf("real(c) = %g\n", real(c)) fmt.Printf("imag(c) = %g\n", imag(c)) fmt.Printf("Conj(a) = %g\n", cmplx.Conj(a)) fmt.Printf("Conj(b) = %g\n", cmplx.Conj(b)) fmt.Printf("Conj(c) = %g\n", cmplx.Conj(c)) }
$ go run ctest1.go a = (1+2i) b = (3-4i) c = (-5-6i) real(a) = 1 imag(a) = 2 real(b) = 3 imag(b) = -4 real(c) = -5 imag(c) = -6 Conj(a) = (1-2i) Conj(b) = (3+4i) Conj(c) = (-5+6i)
複素数は極形式 \(z = r (\cos \theta + i \sin \theta)\) で表すことができます。このとき、r を絶対値、\(\theta\) を偏角といいます。絶対値は関数 cmplx.Abs() で求めることができます。偏角は複素平面において正の実軸とベクトル (x, y) との角度を表します。偏角を求めるには関数 cmplx.Pahse() を使います。返り値 \(\theta\) の範囲は \(-\pi \leq \theta \leq \pi\) (\(\pi\) : 円周率) です。
絶対値と偏角は関数 cmplx.Polar() で求めることもできます。極形式 \(z = r (\cos \theta + i \sin \theta)\) で複素数を生成するときは関数 cmplx.Rect() を使うと便利です。
func Polar(x complex128) (r, θ float64) func Rect(r, θ float64) complex128
Rect() の第 1 引数 r が絶対値、第 2 引数 \(\theta\) が偏角を表します。
簡単な例を示しましょう。
リスト : 複素数の簡単な使用例 (ctest2.c) package main import ( "fmt" "math" "math/cmplx" ) func main() { z := math.Copysign(0.0, -1.0) x := []float64 {-1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 0.0, -1.0, -1.0} y := []float64 { 0.0, 1.0, 1.0, 1.0, 0.0, -1.0, -1.0, -1.0, z} for n := 0; n < 9; n++ { a := complex(x[n], y[n]) fmt.Printf("a = %g\n", a) fmt.Printf("Abs(a) = %g\n", cmplx.Abs(a)) fmt.Printf("Phase(a) = %g\n", cmplx.Phase(a)) } }
$ go run ctest2.go a = (-1+0i) Abs(a) = 1 Phase(a) = 3.141592653589793 a = (-1+1i) Abs(a) = 1.4142135623730951 Phase(a) = 2.356194490192345 a = (0+1i) Abs(a) = 1 Phase(a) = 1.5707963267948966 a = (1+1i) Abs(a) = 1.4142135623730951 Phase(a) = 0.7853981633974483 a = (1+0i) Abs(a) = 1 Phase(a) = 0 a = (1-1i) Abs(a) = 1.4142135623730951 Phase(a) = -0.7853981633974483 a = (0-1i) Abs(a) = 1 Phase(a) = -1.5707963267948966 a = (-1-1i) Abs(a) = 1.4142135623730951 Phase(a) = -2.356194490192345 a = (-1-0i) Abs(a) = 1 Phase(a) = -3.141592653589793
Go 言語の場合、\(-1.0+0.0i\) の偏角 \(\theta\) は \(\pi\) になり、\(-1.0-0.0i\) の偏角は \(-\pi\) になります。ゼロと負のゼロを区別しないプログラミング言語では、偏角 \(\theta\) の範囲を \(-\pi \lt \theta \leq \pi\) に制限して、\(-1.0 + 0.0i (== -1.0 - 0.0i)\) の偏角を \(\pi\) とします。
複素数の四則演算は次のようになります。
これらの演算は Go 言語の演算子 +, -, * , / で行うことができます。複素数の場合、大小の比較演算は使えませんが、等値の判定は演算子 == や != で行うことができます。簡単な例を示しましょう。
リスト : 複素数の使用例 (ctest3.go) package main import ( "fmt" "math/cmplx" ) func main() { a := 1.0 + 2.0i b := 3.0 + 4.0i fmt.Printf("a = %g\n", a) fmt.Printf("b = %g\n", b) fmt.Printf("a + b = %g\n", a + b) fmt.Printf("a - b = %g\n", a - b) fmt.Printf("a * b = %g\n", a * b) fmt.Printf("a / b = %g\n", a / b) fmt.Printf("a == a => %t\n", a == a) fmt.Printf("a != a => %t\n", a != a) fmt.Printf("a == b => %t\n", a == b) fmt.Printf("a != b => %t\n", a != b) c := 1e300 + 1e300i fmt.Printf("c = %g\n", c) fmt.Printf("Abs(c) = %g\n", cmplx.Abs(c)) fmt.Printf("1 / c = %g\n", 1.0 / c) fmt.Printf("c * c = %g\n", c * c) }
$ go run ctest3.go a = (1+2i) b = (3+4i) a + b = (4+6i) a - b = (-2-2i) a * b = (-5+10i) a / b = (0.44+0.08i) a == a => true a != a => false a == b => false a != b => true c = (1e+300+1e+300i) Abs(c) = 1.4142135623730952e+300 1 / c = (5e-301-5e-301i) c * c = (NaN+Infi)
実部と虚部の値は Inf や NaN になることもあります。
複素数を引数にとる指数関数はオイラー (Euler) の公式から導くことができます。
複素数の対数関数は複素数 \(z\) を絶対値 \(|z|\) と偏角 \(\theta\) を使って導くことができます。
複素数 x, y のべき乗 \(x^y\) は次式で求めることができます。
標準ライブラリ math/cmplx には、複素数に対応した関数 Exp(), Log(), Pow() が定義されています。なお、べき乗は演算子 ** でも求めることができます。
func Exp(x complex128) complex128 func Log(x complex128) complex128 func Pow(x, y complex128) complex128
簡単な使用例を示します。
リスト : 複素数の指数関数、対数関数、べき乗 package main import ( "fmt" "math" "math/cmplx" ) func main() { pi := math.Pi fmt.Printf("cmplx.Exp(0 + i pi/4) = %g\n", cmplx.Exp(complex(0, pi / 4))) fmt.Printf("cmplx.Exp(0 + i pi/2) = %g\n", cmplx.Exp(complex(0, pi / 2))) fmt.Printf("cmplx.Exp(0 + i pi) = %g\n", cmplx.Exp(complex(0, pi))) fmt.Printf("cmplx.Exp(0 - i pi) = %g\n", cmplx.Exp(complex(0, -pi))) fmt.Printf("cmplx.Exp(1 + i) = %g\n", cmplx.Exp(complex(1, 1))) fmt.Printf("\n") fmt.Printf("cmplx.Log(1 + i) = %g\n", cmplx.Log(complex(1, 1))) fmt.Printf("cmplx.Log(1 + 0i) = %g\n", cmplx.Log(complex(1, 0))) fmt.Printf("cmplx.Log(0 + i) = %g\n", cmplx.Log(complex(0, 1))) fmt.Printf("cmplx.Log(1 - i) = %g\n", cmplx.Log(complex(1, -1))) fmt.Printf("cmplx.Log(1e300 + i 1e300) = %g\n", cmplx.Log(1e300 + 1e300i)) fmt.Printf("\n") a := 1 + 1i; fmt.Printf("cmplx.Pow(1+i, 0) = %g\n", cmplx.Pow(a, 0 + 0i)) fmt.Printf("cmplx.Pow(1+i, 1) = %g\n", cmplx.Pow(a, 1 + 0i)) fmt.Printf("cmplx.Pow(1+i, 2) = %g\n", cmplx.Pow(a, 2 + 0i)) fmt.Printf("cmplx.Pow(1+i, 3) = %g\n", cmplx.Pow(a, 3 + 0i)) fmt.Printf("cmplx.Pow(1+i, 1+i) = %g\n", cmplx.Pow(a, a)) fmt.Printf("cmplx.Pow(1+2i, 3+4i) = %g\n", cmplx.Pow(1+2i, 3+4i)) fmt.Printf("\n") nz := math.Copysign(0, -1) fmt.Printf("cmplx.Log(-1 + 0i) = %g\n", cmplx.Log(complex(-1, 0))) fmt.Printf("cmplx.Log(-1 - 0i) = %g\n", cmplx.Log(complex(-1, nz))) fmt.Printf("cmplx.Log(-1e300 + 0i) = %g\n", cmplx.Log(complex(-1e300, 0))) fmt.Printf("cmplx.Log(-1e300 - 0i) = %g\n", cmplx.Log(complex(-1e300, nz))) }
$ go run ctest4.go cmplx.Exp(0 + i pi/4) = (0.7071067811865476+0.7071067811865475i) cmplx.Exp(0 + i pi/2) = (6.123233995736757e-17+1i) cmplx.Exp(0 + i pi) = (-1+1.2246467991473515e-16i) cmplx.Exp(0 - i pi) = (-1-1.2246467991473515e-16i) cmplx.Exp(1 + i) = (1.4686939399158851+2.2873552871788423i) cmplx.Log(1 + i) = (0.3465735902799727+0.7853981633974483i) cmplx.Log(1 + 0i) = (0+0i) cmplx.Log(0 + i) = (0+1.5707963267948966i) cmplx.Log(1 - i) = (0.3465735902799727-0.7853981633974483i) cmplx.Log(1e300 + i 1e300) = (691.1221014884936+0.7853981633974483i) cmplx.Pow(1+i, 0) = (1+0i) cmplx.Pow(1+i, 1) = (1.0000000000000002+1i) cmplx.Pow(1+i, 2) = (1.2246467991473517e-16+2.0000000000000004i) cmplx.Pow(1+i, 3) = (-2.0000000000000004+2.0000000000000004i) cmplx.Pow(1+i, 1+i) = (0.2739572538301211+0.5837007587586146i) cmplx.Pow(1+2i, 3+4i) = (0.129009594074467+0.03392409290517015i) cmplx.Log(-1 + 0i) = (0+3.141592653589793i) cmplx.Log(-1 - 0i) = (0-3.141592653589793i) cmplx.Log(-1e300 + 0i) = (690.7755278982137+3.141592653589793i) cmplx.Log(-1e300 - 0i) = (690.7755278982137-3.141592653589793i)
最後の 4 つの例を見てください。関数 \(\log z \ (z = x + iy)\) は負の実軸 (\(-\infty \lt x \lt 0\)) において、\(x + 0.0i\) と \(x - 0.0i\) では値が異なります。数学では値を一つ返す関数を「一価関数」、複数の値を返す関数を「多価関数」といいます。ここで、定義域を制限することで多価関数を一価関数にみなすことを考えます。関数 \(\log z\) の場合、負の実軸を定義域から取り除けば、\(\log z\) を一価関数とみなすことができるわけです。
参考 URL 『逆双曲線関数と逆三角関数の branch cut | 雑記帳』によると、この取り除いた領域を branch cut と呼ぶそうです。プログラミングでは branch cut を定義域から取り除くのではなく、その領域では不連続な関数とするそうです。参考文献『COMMON LISP 第 2 版』では「分枝切断線」、Python のドキュメントでは「分枝切断」と記述されています。本稿では branch cut を「分枝切断」と記述することにします。
プログラミング言語の場合、0.0 と -0.0 を区別する処理系であれば、Go 言語のように 2 つの値を区別することができます。0.0 と -0.0 を区別しない処理系では、偏角 \(\theta\) の範囲を \(-\pi \lt \theta \leq \pi\) に制限することで、\(\log z\) の返り値を (\(-\pi\) を取り除いて) 一つにすることができます。
複素数の三角関数の定義は、オイラーの公式から導かれる式の \(\theta\) を複素数 \(z\) に変えたものになります。
\(\sin z, \cos z\) に純虚数 \(ix\) を与えると双曲線関数 (\(\sinh x, \cosh x\)) になります。
これに三角関数の加法定理 [*1] を使うと次の式が導かれます。
標準ライブラリ math/cmplx に定義されている三角関数 (Sin, Cos, Tan) は複素数に対応しています。
func Sin(x complex128) complex128 func Cos(x complex128) complex128 func Tan(x complex128) complex128
簡単な使用例を示します。
リスト : 複素数の三角関数 (ctest5.c) package main import ( "fmt" "math/cmplx" ) func main() { c := []complex128 {0+1i, 0-1i, 1+1i, 1-1i} for n := 0; n < 4; n++ { a := c[n] b := cmplx.Sin(a) fmt.Printf("a = %g\n", a) fmt.Printf("Sin(a) = %g\n", b) fmt.Printf("Abs(Sin(a)) = %g\n", cmplx.Abs(b)) } fmt.Println("") for n := 0; n < 4; n++ { a := c[n] b := cmplx.Cos(a) fmt.Printf("a = %g\n", a) fmt.Printf("Cos(a) = %g\n", b) fmt.Printf("Abs(Cos(a)) = %g\n", cmplx.Abs(b)) } fmt.Println("") for n := 0; n < 4; n++ { a := c[n] b := cmplx.Tan(a) fmt.Printf("a = %g\n", a) fmt.Printf("Tan(a) = %g\n", b) fmt.Printf("Abs(Tan(a)) = %g\n", cmplx.Abs(b)) } }
$ go run ctest5.go a = (0+1i) Sin(a) = (0+1.1752011936438014i) Abs(Sin(a)) = 1.1752011936438014 a = (0-1i) Sin(a) = (0-1.1752011936438014i) Abs(Sin(a)) = 1.1752011936438014 a = (1+1i) Sin(a) = (1.2984575814159773+0.6349639147847361i) Abs(Sin(a)) = 1.4453965766582497 a = (1-1i) Sin(a) = (1.2984575814159773-0.6349639147847361i) Abs(Sin(a)) = 1.4453965766582497 a = (0+1i) Cos(a) = (1.5430806348152437-0i) Abs(Cos(a)) = 1.5430806348152437 a = (0-1i) Cos(a) = (1.5430806348152437+0i) Abs(Cos(a)) = 1.5430806348152437 a = (1+1i) Cos(a) = (0.8337300251311491-0.9888977057628651i) Abs(Cos(a)) = 1.2934544550420957 a = (1-1i) Cos(a) = (0.8337300251311491+0.9888977057628651i) Abs(Cos(a)) = 1.2934544550420957 a = (0+1i) Tan(a) = (0+0.761594155955765i) Abs(Tan(a)) = 0.761594155955765 a = (0-1i) Tan(a) = (0-0.761594155955765i) Abs(Tan(a)) = 0.761594155955765 a = (1+1i) Tan(a) = (0.2717525853195117+1.0839233273386948i) Abs(Tan(a)) = 1.1174700207060706 a = (1-1i) Tan(a) = (0.2717525853195117-1.0839233273386948i) Abs(Tan(a)) = 1.1174700207060706
複素数の双曲線関数の定義は、実数の定義で引数 x を複素数 z に変えたものになります。sinh z, cosh z に純虚数 ix を与えると三角関数 (sin x, cos x) になります。
これに双曲線関数の加法定理を使うと、次の式が導かれます。
標準ライブラリ math/cmplx に定義されている双曲線関数 (Sinh, Cosh, Tanh) は複素数に対応しています。
func Sinh(x complex128) complex128 func Cosh(x complex128) complex128 func Tanh(x complex128) complex128
簡単な使用例を示します。
リスト : 複素数の双曲線関数 package main import ( "fmt" "math/cmplx" ) func main() { c := []complex128 {0+1i, 0-1i, 1+1i, 1-1i, 0+0i} for n := 0; n < 5; n++ { a := c[n] b := cmplx.Sinh(a) fmt.Printf("a = %g\n", a) fmt.Printf("Sinh(a) = %g\n", b) } fmt.Println("") for n := 0; n < 5; n++ { a := c[n] b := cmplx.Cosh(a) fmt.Printf("a = %g\n", a) fmt.Printf("Cosh(a) = %g\n", b) } fmt.Println("") for n := 0; n < 5; n++ { a := c[n] b := cmplx.Tanh(a) fmt.Printf("a = %g\n", a) fmt.Printf("Tanh(a) = %g\n", b) } }
$ go run ctest6.go a = (0+1i) Sinh(a) = (0+0.8414709848078965i) a = (0-1i) Sinh(a) = (0-0.8414709848078965i) a = (1+1i) Sinh(a) = (0.6349639147847361+1.2984575814159773i) a = (1-1i) Sinh(a) = (0.6349639147847361-1.2984575814159773i) a = (0+0i) Sinh(a) = (0+0i) a = (0+1i) Cosh(a) = (0.5403023058681398+0i) a = (0-1i) Cosh(a) = (0.5403023058681398-0i) a = (1+1i) Cosh(a) = (0.8337300251311491+0.9888977057628651i) a = (1-1i) Cosh(a) = (0.8337300251311491-0.9888977057628651i) a = (0+0i) Cosh(a) = (1+0i) a = (0+1i) Tanh(a) = (0+1.557407724654902i) a = (0-1i) Tanh(a) = (0-1.557407724654902i) a = (1+1i) Tanh(a) = (1.0839233273386948+0.2717525853195117i) a = (1-1i) Tanh(a) = (1.0839233273386948-0.2717525853195117i) a = (0+0i) Tanh(a) = (0+0i)
複素数 z の平方根は次の式で求めることができます。
式 (1) を平方根の主値といいます。角度は \(2\pi\) を足すと同じ角度になるので、式 (2) がもう一つの解になります。三角関数の半角の公式を使うと、式 (1) から次の式が導かれます。
式 (1) を平方根の主値といいます。角度は \(2\pi\) を足すと同じ角度になるので、式 (2) がもう一つの解になります。三角関数の半角の公式を使うと、式 (1) から次の式が導かれます。
標準ライブラリ math/cmplx に定義されている関数 Sqrt は複素数に対応しています。
func Sqrt(x complex128) complex128
簡単な実行例を示します。
リスト : 複素数の平方根 (ctest7.go) package main import ( "fmt" "math" "math/cmplx" ) func main() { c := []complex128 {2+0i, -2+0i, 0+2i, 0-2i, 2+2i, -2-2i} for n := 0; n < 6; n++ { a := c[n] b := cmplx.Sqrt(a) fmt.Printf("a = %g\n", a) fmt.Printf("Sqrt(a) = %g\n", b) fmt.Printf("Sqrt(a) * Sqrt(a) = %g\n", b * b); } z := math.Copysign(0, -1) fmt.Printf("Sqrt(-2+0i) = %g\n", cmplx.Sqrt(complex(-2, 0))); fmt.Printf("Sqrt(-2-0i) = %g\n", cmplx.Sqrt(complex(-2, z))); fmt.Printf("Sqrt(-1e300+0i) = %g\n", cmplx.Sqrt(complex(-1e300, 0))); fmt.Printf("Sqrt(-1e300-0i) = %g\n", cmplx.Sqrt(complex(-1e300, z))); }
$ go run ctest7.go a = (2+0i) Sqrt(a) = (1.4142135623730951+0i) Sqrt(a) * Sqrt(a) = (2.0000000000000004+0i) a = (-2+0i) Sqrt(a) = (0+1.4142135623730951i) Sqrt(a) * Sqrt(a) = (-2.0000000000000004+0i) a = (0+2i) Sqrt(a) = (1+1i) Sqrt(a) * Sqrt(a) = (0+2i) a = (0-2i) Sqrt(a) = (1-1i) Sqrt(a) * Sqrt(a) = (0-2i) a = (2+2i) Sqrt(a) = (1.5537739740300374+0.6435942529055826i) Sqrt(a) * Sqrt(a) = (2.0000000000000004+2i) a = (-2-2i) Sqrt(a) = (0.6435942529055826-1.5537739740300374i) Sqrt(a) * Sqrt(a) = (-2.0000000000000004-2i) Sqrt(-2+0i) = (0+1.4142135623730951i) Sqrt(-2-0i) = (0-1.4142135623730951i) Sqrt(-1e300+0i) = (0+1e+150i) Sqrt(-1e300-0i) = (0-1e+150i)
Sqrt は Log と同じ分枝切断を持っています。x を負の整数とすると Sqrt(x) の解は \(i \sqrt x\) になりますが、もうひとつ \(-i \sqrt x\) という解があります。
三角関数の逆関数を「逆三角関数 (inverse trigonometric function)」といいます。以下に標準ライブラリ math に定義されている逆三角関数を示します。
ここでは引数 x を実数とします。Asin x は引数 x が与えられたとき Sin w = x となる角度 w を求めます。同様に Acos x は Cos w = x となる角度 w を、Atan x は Tan x = w となる角度 w を求めます。三角関数には周期性があるので、上式を満たす角度 w は無数に存在します。つまり、逆三角関数の返り値は無数にあることになりますが、通常は一つの値を返すように範囲を制限します。これを「主値」といいます。
逆三角関数の主値を以下に示します。
本ページでは、数式の表示に JavaScript のライブラリ MathJax を使っています。MathJax では、逆三角関数を arcsin, arccos, arctan と表示します。
簡単な実行例を示します。
リスト : 逆三角関数 (ctest8.go) package main import ( "fmt" "math" ) func main() { for x := -1.0; x <= 1.0; x += 0.5 { fmt.Printf("Asin(%g) = %g\n", x, math.Asin(x)); } for x := -1.0; x <= 1.0; x += 0.5 { fmt.Printf("Acos(%g) = %g\n", x, math.Acos(x)); } for x := -2.0; x <= 2.0; x += 1.0 { fmt.Printf("Atan(%g) = %g\n", x, math.Atan(x)); } }
$ go run ctest8.go Asin(-1) = -1.5707963267948966 Asin(-0.5) = -0.5235987755982989 Asin(0) = 0 Asin(0.5) = 0.5235987755982989 Asin(1) = 1.5707963267948966 Acos(-1) = 3.141592653589793 Acos(-0.5) = 2.0943951023931957 Acos(0) = 1.5707963267948966 Acos(0.5) = 1.0471975511965976 Acos(1) = 0 Atan(-2) = -1.1071487177940904 Atan(-1) = -0.7853981633974483 Atan(0) = 0 Atan(1) = 0.7853981633974483 Atan(2) = 1.1071487177940904
math には関数 Atan2 も用意されています。
func Atan2(y, x float64) float64
引数 x, y は実数です。Atan2 は直交座標系においてベクトル (x, y) と x 軸との角度を求める関数です。複素平面で考えると、複素数 \(x + iy\) の偏角 \(\theta\) を求めることと同じです。返り値 (角度 \(\theta\)) の範囲は \(-\pi \leq \theta \leq \pi\) になります。
簡単な使用例を示します。
リスト : Atan2() の使用例 (ctest81.go) package main import ( "fmt" "math" ) func main() { z := math.Copysign(0, -1) x := []float64 {-1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 0.0, -1.0, -1.0} y := []float64 { 0.0, 1.0, 1.0, 1.0, 0.0, -1.0, -1.0, -1.0, z} for n := 0; n < 9; n++ { fmt.Printf("Atan2(%g, %g) = %g\n", y[n], x[n], math.Atan2(y[n], x[n])); } }
$ go run ctest81.go Atan2(0, -1) = 3.141592653589793 Atan2(1, -1) = 2.356194490192345 Atan2(1, 0) = 1.5707963267948966 Atan2(1, 1) = 0.7853981633974483 Atan2(0, 1) = 0 Atan2(-1, 1) = -0.7853981633974483 Atan2(-1, 0) = -1.5707963267948966 Atan2(-1, -1) = -2.356194490192345 Atan2(-0, -1) = -3.141592653589793
複素数の逆三角関数の定義は、複素数の三角関数の定義から導くことができます。Asin z の定義は次のようになります。
\(\arccos z, \arctan z\) は定義だけを示します。
Asin, Acos, Atan は次に示す分枝切断を持っています。
標準ライブラリ math/cmplx に定義されている逆三角関数 (Asin, Acos, Atan) は複素数に対応しています。
func Asin(x complex128) complex128 func Acos(x complex128) complex128 func Atan(x complex128) complex128
簡単な実行例を示します。
リスト : 複素数の逆三角関数 (ctest9.go) package main import ( "fmt" "math" "math/cmplx" ) func main() { z := math.Copysign(0, -1); c := []complex128 {2+0i, -2+0i, 0+2i, 0-2i, 2+2i, -2-2i} for n := 0; n < 6; n++ { a := c[n] b := cmplx.Asin(a) fmt.Printf("a = %g\n", a) fmt.Printf("Asin(a) = %g\n", b) fmt.Printf("Sin(Asin(a)) = %g\n", cmplx.Sin(b)) } fmt.Println(""); fmt.Printf("Asin(4+0.0i) = %g\n", cmplx.Asin(complex(4, 0))) fmt.Printf("Asin(4-0.0i) = %g\n", cmplx.Asin(complex(4, z))) fmt.Printf("Asin(-4+0.0i) = %g\n", cmplx.Asin(complex(-4, 0))) fmt.Printf("Asin(-4-0.0i) = %g\n", cmplx.Asin(complex(-4, z))) fmt.Println("") for n := 0; n < 6; n++ { a := c[n] b := cmplx.Acos(a) fmt.Printf("a = %g\n", a) fmt.Printf("Acos(a) = %g\n", b) fmt.Printf("Cos(Acos(a)) = %g\n", cmplx.Cos(b)) } fmt.Println("") fmt.Printf("Acos(4+0.0i) = %g\n", cmplx.Acos(complex(4, 0))) fmt.Printf("Acos(4-0.0i) = %g\n", cmplx.Acos(complex(4, z))) fmt.Printf("Acos(-4+0.0i) = %g\n", cmplx.Acos(complex(-4, 0))) fmt.Printf("Acos(-4-0.0i) = %g\n", cmplx.Acos(complex(-4, z))) fmt.Println("") for n := 0; n < 6; n++ { a := c[n] b := cmplx.Atan(a) fmt.Printf("a = %g\n", a) fmt.Printf("Atan(a) = %g\n", b) fmt.Printf("Tan(Atan(a)) = %g\n", cmplx.Tan(b)) } fmt.Println("") fmt.Printf("Atan(0+4i) = %g\n", cmplx.Atan(complex(0, 4))) fmt.Printf("Atan(-0+4i) = %g\n", cmplx.Atan(complex(z, 4))) fmt.Printf("Atan(0-4i) = %g\n", cmplx.Atan(complex(0, -4))) fmt.Printf("Atan(-0-4i) = %g\n", cmplx.Atan(complex(z, -4))) fmt.Println(""); }
$ go run ctest9.go a = (2+0i) Asin(a) = (1.5707963267948966+1.3169578969248164i) Sin(Asin(a)) = (1.9999999999999993+1.060575238724905e-16i) a = (-2+0i) Asin(a) = (-1.5707963267948966+1.3169578969248164i) Sin(Asin(a)) = (-1.9999999999999993+1.060575238724905e-16i) a = (0+2i) Asin(a) = (0+1.4436354751788099i) Sin(Asin(a)) = (0+1.9999999999999987i) a = (0-2i) Asin(a) = (0-1.4436354751788103i) Sin(Asin(a)) = (0-2i) a = (2+2i) Asin(a) = (0.7542491446980463+1.734324521487968i) Sin(Asin(a)) = (2.000000000000003+2.000000000000002i) a = (-2-2i) Asin(a) = (-0.7542491446980462-1.7343245214879666i) Sin(Asin(a)) = (-2.0000000000000004-2i) Asin(4+0.0i) = (1.5707963267948966+2.0634370688955617i) Asin(4-0.0i) = (1.5707963267948966-2.0634370688955608i) Asin(-4+0.0i) = (-1.5707963267948966+2.0634370688955617i) Asin(-4-0.0i) = (-1.5707963267948966-2.0634370688955608i) a = (2+0i) Acos(a) = (0-1.3169578969248164i) Cos(Acos(a)) = (1.9999999999999993+0i) a = (-2+0i) Acos(a) = (3.141592653589793-1.3169578969248164i) Cos(Acos(a)) = (-1.9999999999999993+2.12115047744981e-16i) a = (0+2i) Acos(a) = (1.5707963267948966-1.4436354751788099i) Cos(Acos(a)) = (1.3691967456605042e-16+1.9999999999999991i) a = (0-2i) Acos(a) = (1.5707963267948966+1.4436354751788103i) Cos(Acos(a)) = (1.3691967456605047e-16-2i) a = (2+2i) Acos(a) = (0.8165471820968503-1.734324521487968i) Cos(Acos(a)) = (2.0000000000000036+2.000000000000002i) a = (-2-2i) Acos(a) = (2.3250454714929427+1.7343245214879666i) Cos(Acos(a)) = (-2-2i) Acos(4+0.0i) = (0-2.0634370688955617i) Acos(4-0.0i) = (0+2.0634370688955608i) Acos(-4+0.0i) = (3.141592653589793-2.0634370688955617i) Acos(-4-0.0i) = (3.141592653589793+2.0634370688955608i) a = (2+0i) Atan(a) = (1.1071487177940904+0i) Tan(Atan(a)) = (1.9999999999999996+0i) a = (-2+0i) Atan(a) = (-1.1071487177940904+0i) Tan(Atan(a)) = (-1.9999999999999996+0i) a = (0+2i) Atan(a) = (-1.5707963267948968+0.5493061443340549i) Tan(Atan(a)) = (4.82436794902991e-16+1.9999999999999993i) a = (0-2i) Atan(a) = (-1.5707963267948968-0.5493061443340549i) Tan(Atan(a)) = (4.82436794902991e-16-1.9999999999999993i) a = (2+2i) Atan(a) = (1.311223269671635+0.23887786125685911i) Tan(Atan(a)) = (1.9999999999999993+1.9999999999999991i) a = (-2-2i) Atan(a) = (-1.311223269671635-0.23887786125685906i) Tan(Atan(a)) = (-1.9999999999999998-1.999999999999999i) Atan(0+4i) = (-1.5707963267948968+0.25541281188299536i) // 実部の符号が逆 Atan(-0+4i) = (-1.5707963267948966+0.25541281188299536i) Atan(0-4i) = (-1.5707963267948968-0.25541281188299536i) // 実部の符号が逆 Atan(-0-4i) = (-1.5707963267948966-0.25541281188299536i)
Go 言語 (ver 1.17.4, 1.23.2) の Atan() には分枝切断に不具合があるようです。
双曲線関数の逆関数を「逆双曲線関数 (inverse hyperbolic function)」といいます。標準ライブラリ math には逆双曲線関数 Asinh, Acosh, Atanh が定義されています。MathJax では逆双曲線関数を \(\sinh^{-1} x, \cosh^{-1} x, \tanh^{-1} x \) で表します。双曲線関数と逆双曲線関数の定義域と値域を示します。
x と y は実数です。x = Cosh y の逆関数 y = Acosh x を満たす y の値は 2 つありますが、ここでは \(y \geq 0\) を主値として選ぶことにします。
逆双曲線関数の定義は双曲線関数の定義から導くことができます。
また、次の関係式を使って Atanh から Asinh と Acosh を求めることができます。
簡単な実行例を示します。
リスト : 逆双曲線関数 (ctest10.go) package main import ( "fmt" "math" ) func main() { for x := -2.0; x <= 2.0; x += 0.5 { fmt.Printf("Asinh(%g) = %g\n", x, math.Asinh(x)) fmt.Printf("Sinh(Asinh(%g)) = %g\n", x, math.Sinh(math.Asinh(x))) } fmt.Println("") for x := 1.0; x <= 4.0; x += 0.5 { fmt.Printf("Acosh(%g) = %g\n", x, math.Acosh(x)) fmt.Printf("Cosh(Acosh(%g)) = %g\n", x, math.Cosh(math.Acosh(x))) } fmt.Println("") for x := -0.75; x <= 0.75; x += 0.25 { fmt.Printf("Atanh(%g) = %g\n", x, math.Atanh(x)) fmt.Printf("Tanh(Atanh(%g)) = %g\n", x, math.Tanh(math.Atanh(x))) } }
$ go run ctest10.go Asinh(-2) = -1.4436354751788103 Sinh(Asinh(-2)) = -2 Asinh(-1.5) = -1.1947632172871094 Sinh(Asinh(-1.5)) = -1.5 Asinh(-1) = -0.881373587019543 Sinh(Asinh(-1)) = -0.9999999999999999 Asinh(-0.5) = -0.48121182505960347 Sinh(Asinh(-0.5)) = -0.5 Asinh(0) = 0 Sinh(Asinh(0)) = 0 Asinh(0.5) = 0.48121182505960347 Sinh(Asinh(0.5)) = 0.5 Asinh(1) = 0.881373587019543 Sinh(Asinh(1)) = 0.9999999999999999 Asinh(1.5) = 1.1947632172871094 Sinh(Asinh(1.5)) = 1.5 Asinh(2) = 1.4436354751788103 Sinh(Asinh(2)) = 2 Acosh(1) = 0 Cosh(Acosh(1)) = 1 Acosh(1.5) = 0.9624236501192069 Cosh(Acosh(1.5)) = 1.5 Acosh(2) = 1.3169578969248166 Cosh(Acosh(2)) = 1.9999999999999998 Acosh(2.5) = 1.566799236972411 Cosh(Acosh(2.5)) = 2.5 Acosh(3) = 1.7627471740390859 Cosh(Acosh(3)) = 2.9999999999999996 Acosh(3.5) = 1.9248473002384139 Cosh(Acosh(3.5)) = 3.5 Acosh(4) = 2.0634370688955608 Cosh(Acosh(4)) = 4.000000000000001 Atanh(-0.75) = -0.9729550745276566 Tanh(Atanh(-0.75)) = -0.75 Atanh(-0.5) = -0.5493061443340548 Tanh(Atanh(-0.5)) = -0.49999999999999994 Atanh(-0.25) = -0.25541281188299536 Tanh(Atanh(-0.25)) = -0.25 Atanh(0) = 0 Tanh(Atanh(0)) = 0 Atanh(0.25) = 0.25541281188299536 Tanh(Atanh(0.25)) = 0.25 Atanh(0.5) = 0.5493061443340548 Tanh(Atanh(0.5)) = 0.49999999999999994 Atanh(0.75) = 0.9729550745276566 Tanh(Atanh(0.75)) = 0.75
複素数の逆双曲線関数の定義は、実数の定義で引数 x を複素数 z に変えたものになります。
参考 URL 『逆双曲線関数 - Wikipedia』によると、\(\cosh^{-1} z\) を式 (1) でプログラムすると「分枝切断線」が複雑になるため、他の式 (たとえば (2) など) でプログラムする処理系が多いようです。ちなみに、ANSI Common Lisp では \(\cosh^{-1} z\) を次の式で定義しています。
複素数の逆双曲線関数は次に示す分枝切断を持っています。
標準ヘッダファイル math/cmplx の逆双曲線関数 (Asinh, Acosh, Atanh) は複素数に対応しています。
func Asinh(x complex128) complex128 func Acosh(x complex128) complex128 func Atanh(x complex128) complex128
簡単な実行例を示します。
リスト : 複素数の逆双曲線関数 (ctest11.go) package main import ( "fmt" "math" "math/cmplx" ) func main() { c := []complex128 {2+0i, -2+0i, 0+2i, 0-2i, 2+2i, -2-2i} z := math.Copysign(0, -1) for n := 0; n < 6; n++ { a := c[n] b := cmplx.Asinh(a) fmt.Printf("a = %g\n", a) fmt.Printf("Asinh(a) = %g\n", b) fmt.Printf("Sinh(Asinh(a)) = %g\n", cmplx.Sinh(b)) } fmt.Println(""); fmt.Printf("Asinh(0.0+2.0i) = %g\n", cmplx.Asinh(complex(0, 2))) fmt.Printf("Asinh(-0.0+2.0i) = %g\n", cmplx.Asinh(complex(z, 2))) fmt.Printf("Asinh(0.0-4.0i) = %g\n", cmplx.Asinh(complex(0, -4))) fmt.Printf("Asinh(-0.0-4.0i) = %g\n", cmplx.Asinh(complex(z, -4))) fmt.Println("") for n := 0; n < 6; n++ { a := c[n] b := cmplx.Acosh(a) fmt.Printf("a = %g\n", a) fmt.Printf("Acosh(a) = %g\n", b) fmt.Printf("Cosh(Acosh(a)) = %g\n", cmplx.Cosh(b)) } fmt.Println("") fmt.Printf("Acosh(0+0i) = %g\n", cmplx.Acosh(complex(0, 0))) fmt.Printf("Acosh(0-0i) = %g\n", cmplx.Acosh(complex(0, z))) fmt.Printf("Acosh(-4+0i) = %g\n", cmplx.Acosh(complex(-4, 0))) fmt.Printf("Acosh(-4-0i) = %g\n", cmplx.Acosh(complex(-4, z))) fmt.Printf("\n") for n := 0; n < 6; n++ { a := c[n] b := cmplx.Atanh(a) fmt.Printf("a = %g\n", a) fmt.Printf("Atanh(a) = %g\n", b) fmt.Printf("Tanh(Atanh(a)) = %g\n", cmplx.Tanh(b)) } fmt.Println("") fmt.Printf("Atanh(2+0i) = %g\n", cmplx.Atanh(complex(2, 0))) fmt.Printf("Atanh(2-0i) = %g\n", cmplx.Atanh(complex(2, z))) fmt.Printf("Atanh(-4+0i) = %g\n", cmplx.Atanh(complex(-4, 0))) fmt.Printf("Atanh(-4-0i) = %g\n", cmplx.Atanh(complex(-4, z))) fmt.Println(""); }
$ go run ctest11.go a = (2+0i) Asinh(a) = (1.4436354751788103+0i) Sinh(Asinh(a)) = (2+0i) a = (-2+0i) Asinh(a) = (-1.4436354751788099+0i) Sinh(Asinh(a)) = (-1.9999999999999991+0i) a = (0+2i) Asinh(a) = (1.3169578969248166+1.5707963267948966i) Sinh(Asinh(a)) = (1.0605752387249052e-16+1.9999999999999998i) a = (0-2i) Asinh(a) = (1.3169578969248166-1.5707963267948966i) Sinh(Asinh(a)) = (1.0605752387249052e-16-1.9999999999999998i) a = (2+2i) Asinh(a) = (1.7343245214879666+0.7542491446980462i) Sinh(Asinh(a)) = (2+2.0000000000000004i) a = (-2-2i) Asinh(a) = (-1.734324521487968-0.7542491446980463i) Sinh(Asinh(a)) = (-2.000000000000002-2.0000000000000036i) Asinh(0.0+2.0i) = (1.3169578969248166+1.5707963267948966i) Asinh(-0.0+2.0i) = (-1.3169578969248164+1.5707963267948966i) Asinh(0.0-4.0i) = (2.0634370688955608-1.5707963267948966i) Asinh(-0.0-4.0i) = (-2.0634370688955617-1.5707963267948966i) a = (2+0i) Acosh(a) = (1.3169578969248164+0i) Cosh(Acosh(a)) = (1.9999999999999993+0i) a = (-2+0i) Acosh(a) = (1.3169578969248164+3.141592653589793i) Cosh(Acosh(a)) = (-1.9999999999999993+2.12115047744981e-16i) a = (0+2i) Acosh(a) = (1.4436354751788099+1.5707963267948966i) Cosh(Acosh(a)) = (1.369196745660504e-16+1.9999999999999987i) a = (0-2i) Acosh(a) = (1.4436354751788103-1.5707963267948966i) Cosh(Acosh(a)) = (1.3691967456605047e-16-2i) a = (2+2i) Acosh(a) = (1.734324521487968+0.8165471820968503i) Cosh(Acosh(a)) = (2.0000000000000036+2.000000000000002i) a = (-2-2i) Acosh(a) = (1.7343245214879666-2.3250454714929427i) Cosh(Acosh(a)) = (-2-2i) Acosh(0+0i) = (0+1.5707963267948966i) Acosh(0-0i) = (0-1.5707963267948966i) Acosh(-4+0i) = (2.0634370688955617+3.141592653589793i) Acosh(-4-0i) = (2.0634370688955608-3.141592653589793i) a = (2+0i) Atanh(a) = (0.5493061443340549+1.5707963267948966i) Tanh(Atanh(a)) = (1.9999999999999993+1.8369701987210265e-16i) a = (-2+0i) Atanh(a) = (-0.5493061443340549+1.5707963267948966i) Tanh(Atanh(a)) = (-1.9999999999999993+1.8369701987210265e-16i) a = (0+2i) Atanh(a) = (0+1.1071487177940904i) Tanh(Atanh(a)) = (0+1.9999999999999996i) a = (0-2i) Atanh(a) = (0-1.1071487177940904i) Tanh(Atanh(a)) = (0-1.9999999999999996i) a = (2+2i) Atanh(a) = (0.23887786125685911+1.311223269671635i) Tanh(Atanh(a)) = (1.9999999999999998+2i) a = (-2-2i) Atanh(a) = (-0.23887786125685906-1.311223269671635i) Tanh(Atanh(a)) = (-1.9999999999999991-2i) Atanh(2+0i) = (0.5493061443340549+1.5707963267948966i) Atanh(2-0i) = (0.5493061443340549+1.5707963267948968i) // 虚部の符号が逆 Atanh(-4+0i) = (-0.25541281188299536+1.5707963267948966i) Atanh(-4-0i) = (-0.25541281188299536+1.5707963267948968i) // 虚部の符号が逆
Go 言語 (ver 1.17.4, 1.23.2) の Atanh() には分枝切断に不具合があるようです。