[インデックス 13843] ファイルの概要
このコミットは、Go言語の標準ライブラリsrc/pkg/math/tanh.go
ファイルに対する変更です。このファイルは、数値計算における双曲線正接(hyperbolic tangent, tanh)関数を実装しています。tanh
関数は、シグモイド関数の一種であり、ニューラルネットワークの活性化関数や信号処理など、様々な科学技術計算で利用されます。
コミット
このコミットは、math.Tanh
関数の実装を改善し、パフォーマンスと精度を向上させることを目的としています。具体的には、既存のSinh
とCosh
関数を用いた実装から、Cephes Math Libraryのアルゴリズムに基づくより高速で高精度な有理関数近似へと変更されています。これにより、Tanh
関数の計算速度が大幅に向上し、同時に精度もわずかに改善されました。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0f8f5d2140120d00fee53c633e82265d6d4e6784
元コミット内容
math: Faster Tanh
From 159 to 47.6 ns/op; slightly more accurate.
R=rsc, golang-dev, mtj, dave, remyoudompheng
CC=golang-dev
https://golang.org/cl/6500121
変更の背景
この変更の主な背景は、math.Tanh
関数のパフォーマンスと精度の向上です。元の実装では、Tanh(x)
をSinh(x) / Cosh(x)
として計算していました。しかし、このアプローチにはいくつかの課題がありました。
- パフォーマンス:
Sinh
とCosh
関数はそれぞれ指数関数Exp
を用いて計算されるため、Tanh
の計算には複数のExp
呼び出しが必要となり、計算コストが高くなりがちでした。コミットメッセージにあるように、変更前は159 ns/opかかっていた計算が、変更後には47.6 ns/opへと大幅に短縮されています。これは約3.3倍の高速化に相当します。 - 精度:
Sinh(x) / Cosh(x)
という形式は、x
が非常に大きい場合にオーバーフローやアンダーフローを引き起こす可能性があり、また、浮動小数点数の精度限界により、特定の範囲で誤差が蓄積しやすいという問題がありました。特にx
が0に近い場合、Sinh(x)
とCosh(x)
が非常に小さな値になるため、除算の精度が問題となることがあります。 - 既存の数学ライブラリの活用: 多くの高性能な数学ライブラリ(例えばCephes Math Library)では、
tanh
関数に対して、より効率的で高精度な数値計算アルゴリズムが採用されています。これらのアルゴリズムは、特定の範囲で有理関数近似を用いるなど、浮動小数点数の特性を考慮した最適化が施されています。このコミットは、そのような既存の知見を取り入れることで、Go言語の標準ライブラリの品質を向上させることを目指しました。
この変更により、math.Tanh
関数はより高速かつ高精度になり、Go言語で数値計算を行うアプリケーションの全体的なパフォーマンスと信頼性の向上に貢献します。
前提知識の解説
双曲線正接関数 (Hyperbolic Tangent, tanh)
双曲線正接関数 tanh(x)
は、数学における双曲線関数の一つで、以下のように定義されます。
tanh(x) = sinh(x) / cosh(x)
ここで、sinh(x)
は双曲線正弦関数、cosh(x)
は双曲線余弦関数であり、それぞれ以下のように定義されます。
sinh(x) = (e^x - e^(-x)) / 2
cosh(x) = (e^x + e^(-x)) / 2
これらの定義から、tanh(x)
は以下のように書き換えることができます。
tanh(x) = (e^x - e^(-x)) / (e^x + e^(-x))
この関数は、-1
から 1
の範囲の値を取り、x
が大きくなるにつれて 1
に、x
が小さくなるにつれて -1
に漸近します。x = 0
のとき tanh(0) = 0
となります。そのS字型の形状から、ニューラルネットワークの活性化関数として広く利用されています。
有理関数近似 (Rational Function Approximation)
有理関数近似とは、ある関数を、2つの多項式の比(有理関数)で近似する方法です。
R(x) = P(x) / Q(x)
ここで P(x)
と Q(x)
は多項式です。有理関数近似は、テイラー級数展開などの多項式近似と比較して、より広い範囲で高い精度を達成できる場合があります。特に、関数の特異点付近や漸近挙動を持つ関数に対して有効です。
tanh(x)
の場合、x
が小さい範囲では x + x^3 P(x)/Q(x)
の形式が用いられることがあります。これは、tanh(x)
が x
の周りで奇関数である(tanh(-x) = -tanh(x)
)という性質を利用したもので、x
の奇数乗のみを含む多項式で近似することで、計算効率と精度を両立させます。
Cephes Math Library
Cephes Math Libraryは、Stephen L. Moshierによって開発された、C言語で書かれた高品質な数学関数ライブラリです。このライブラリは、様々な特殊関数や初等関数(三角関数、指数関数、対数関数など)の高精度な実装を提供しており、科学技術計算や数値解析の分野で広く利用されています。
Cephesライブラリの関数は、多くの場合、特定の入力範囲に対して異なるアルゴリズム(例えば、有理関数近似や多項式近似)を適用することで、計算効率と精度を最適化しています。このコミットで導入されたtanh
関数の実装も、Cephesライブラリのtanh.c
を参考にしています。
技術的詳細
新しいmath.Tanh
関数の実装は、入力値 x
の絶対値 z = Abs(x)
に応じて、以下の3つのケースに分岐して計算を行います。
-
z > 0.5 * MAXLOG
の場合:MAXLOG
は8.8029691931113054295988e+01
であり、これはlog(2^127)
に相当します。これは、float64
の最大値に近い値であり、exp(2*z)
がオーバーフローする可能性のある非常に大きなx
の範囲をカバーします。- この範囲では、
tanh(x)
はx
が正であれば1
に、負であれば-1
に非常に近くなります。そのため、x
の符号に応じて1
または-1
を直接返します。これにより、不必要な計算や浮動小数点数の精度限界による誤差を避けることができます。
-
z >= 0.625
の場合:- この範囲では、
tanh(x)
の定義式tanh(x) = 1 - 2 / (exp(2x) + 1)
を利用して計算します。 - まず
s = Exp(2 * z)
を計算します。 - 次に
z = 1 - 2 / (s + 1)
を計算します。 - 最後に、元の
x
の符号に応じてz
の符号を調整します(x < 0
ならばz = -z
)。 - この式は、
x
が十分に大きい場合にtanh(x)
が1
に漸近するという性質を効率的に利用しています。exp(2x)
が十分に大きくなると、2 / (exp(2x) + 1)
は0
に近づくため、tanh(x)
は1
に近づきます。
- この範囲では、
-
上記以外のケース (つまり
z < 0.625
):- この範囲は
x
が0
に近い場合をカバーします。 - まず、
x == 0
の場合はx
(つまり0
) をそのまま返します。これはtanh(0) = 0
であるため、正確な結果を返します。 - それ以外の場合、有理関数近似
x + x*s*((tanhP[0]*s+tanhP[1])*s+tanhP[2])/(((s+tanhQ[0])*s+tanhQ[1])*s+tanhQ[2])
を使用してtanh(x)
を計算します。- ここで
s = x * x
です。これは、tanh(x)
が奇関数であるため、x
の偶数乗のみを含む多項式で近似することで、計算を効率化しています。 tanhP
とtanhQ
は、それぞれ分子と分母の多項式の係数を格納するfloat64
型の配列です。var tanhP = [...]float64{ -9.64399179425052238628E-1, -9.92877231001918586564E1, -1.61468768441708447952E3, } var tanhQ = [...]float64{ 1.12811678491632931402E2, 2.23548839060100448583E3, 4.84406305325125486048E3, }
- これらの係数は、Cephes Math Libraryの
tanh.c
から引用されたもので、特定の精度要件を満たすように設計されています。この形式は、Horner法に似た効率的な多項式評価を可能にします。
- ここで
- この範囲は
この多分岐アプローチにより、tanh
関数は入力値の範囲に応じて最適なアルゴリズムを選択し、全体として高速かつ高精度な計算を実現しています。
コアとなるコードの変更箇所
--- a/src/pkg/math/tanh.go
+++ b/src/pkg/math/tanh.go
@@ -4,12 +4,66 @@
package math
-/*
- Floating-point hyperbolic tangent.
+// The original C code, the long comment, and the constants
+// below were from http://netlib.sandia.gov/cephes/cmath/sin.c,
+// available from http://www.netlib.org/cephes/cmath.tgz.
+// The go code is a simplified version of the original C.
+// tanh.c
+//
+// Hyperbolic tangent
+//
+// SYNOPSIS:
+//
+// double x, y, tanh();
+//
+// y = tanh( x );
+//
+// DESCRIPTION:
+//
+// Returns hyperbolic tangent of argument in the range MINLOG to MAXLOG.
+// MAXLOG = 8.8029691931113054295988e+01 = log(2**127)
+// MINLOG = -8.872283911167299960540e+01 = log(2**-128)
+//
+// A rational function is used for |x| < 0.625. The form
+// x + x**3 P(x)/Q(x) of Cody & Waite is employed.
+// Otherwise,
+// tanh(x) = sinh(x)/cosh(x) = 1 - 2/(exp(2x) + 1).
+//
+// ACCURACY:
+//
+// Relative error:
+// arithmetic domain # trials peak rms
+// IEEE -2,2 30000 2.5e-16 5.8e-17
+//
+// Cephes Math Library Release 2.8: June, 2000
+// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
+//
+// The readme file at http://netlib.sandia.gov/cephes/ says:
+// Some software in this archive may be from the book _Methods and
+// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
+// International, 1989) or from the Cephes Mathematical Library, a
+// commercial product. In either event, it is copyrighted by the author.
+// What you see here may be used freely but it comes with no support or
+// guarantee.
+//
+// The two known misprints in the book are repaired here in the
+// source listings for the gamma function and the incomplete beta
+// integral.
+//
+// Stephen L. Moshier
+// moshier@na-net.ornl.gov
+//
-\tSinh and Cosh are called except for large arguments, which\n-\twould cause overflow improperly.\n-*/\n+var tanhP = [...]float64{\n+\t-9.64399179425052238628E-1,\n+\t-9.92877231001918586564E1,\n+\t-1.61468768441708447952E3,\n+}\n+var tanhQ = [...]float64{\n+\t1.12811678491632931402E2,\n+\t2.23548839060100448583E3,\n+\t4.84406305325125486048E3,\n+}\n \n // Tanh computes the hyperbolic tangent of x.\n //
@@ -18,15 +72,26 @@ package math\n //\tTanh(±Inf) = ±1\n //\tTanh(NaN) = NaN\n func Tanh(x float64) float64 {\n-\tif x < 0 {\n-\t\tx = -x\n-\t\tif x > 21 {\n+\tconst MAXLOG = 8.8029691931113054295988e+01 // log(2**127)\n+\tz := Abs(x)\n+\tswitch {\n+\tcase z > 0.5*MAXLOG:\n+\t\tif x < 0 {\n \t\t\treturn -1\n \t\t}\n-\t\treturn -Sinh(x) / Cosh(x)\n-\t}\n-\tif x > 21 {\n \t\treturn 1\n+\tcase z >= 0.625:\n+\t\ts := Exp(2 * z)\n+\t\tz = 1 - 2/(s+1)\n+\t\tif x < 0 {\n+\t\t\tz = -z\n+\t\t}\n+\tdefault:\n+\t\tif x == 0 {\n+\t\t\treturn x\n+\t\t}\n+\t\ts := x * x\n+\t\tz = x + x*s*((tanhP[0]*s+tanhP[1])*s+tanhP[2])/(((s+tanhQ[0])*s+tanhQ[1])*s+tanhQ[2])\n \t}\n-\treturn Sinh(x) / Cosh(x)\n+\treturn z\n }\n```
## コアとなるコードの解説
変更された`Tanh`関数の実装は、以下の主要な部分から構成されます。
1. **定数と係数の定義**:
* `MAXLOG` 定数: `8.8029691931113054295988e+01` は、`log(2^127)` に相当し、`exp(2x)` がオーバーフローする可能性のある境界値として使用されます。
* `tanhP` と `tanhQ` 配列: これらは、`x` が小さい場合の有理関数近似で使用される多項式の係数です。これらの値は、Cephes Math Libraryから引用されており、高い精度を保証するために慎重に選択されています。
2. **入力値の絶対値の取得**:
* `z := Abs(x)`: 入力 `x` の絶対値 `z` を計算します。`tanh`関数は奇関数であるため、絶対値で計算し、最後に符号を調整するアプローチは一般的であり、効率的です。
3. **条件分岐によるアルゴリズム選択**:
* `switch` ステートメントを使用して、`z` の値に基づいて異なる計算パスを選択します。
* **ケース 1: `z > 0.5 * MAXLOG` (非常に大きな入力)**
```go
case z > 0.5*MAXLOG:
if x < 0 {
return -1
}
return 1
```
`x` の絶対値が非常に大きい場合、`tanh(x)` は `1` (x > 0) または `-1` (x < 0) に非常に近くなります。このケースでは、計算を省略し、直接 `1` または `-1` を返します。これにより、オーバーフローのリスクを回避し、最高のパフォーマンスと十分な精度を確保します。
* **ケース 2: `z >= 0.625` (中程度の入力)**
```go
case z >= 0.625:
s := Exp(2 * z)
z = 1 - 2/(s+1)
if x < 0 {
z = -z
}
```
`x` の絶対値が `0.625` 以上で、かつ非常に大きくない場合、`tanh(x) = 1 - 2 / (exp(2x) + 1)` の公式を使用します。`Exp`関数はGoの`math`パッケージで提供される指数関数です。計算された `z` は、最後に元の `x` の符号に基づいて調整されます。この方法は、`x` が `0` から離れるにつれて `tanh(x)` が `1` に漸近するという性質を効率的に利用します。
* **ケース 3: `default` (小さな入力)**
```go
default:
if x == 0 {
return x
}
s := x * x
z = x + x*s*((tanhP[0]*s+tanhP[1])*s+tanhP[2])/(((s+tanhQ[0])*s+tanhQ[1])*s+tanhQ[2])
```
`x` の絶対値が `0.625` 未満の場合、つまり `x` が `0` に近い場合、有理関数近似を使用します。
* `if x == 0 { return x }` は、`tanh(0) = 0` の特殊ケースを正確に処理します。
* `s := x * x` は、`x` の偶数乗を効率的に計算するための準備です。
* `z = x + x*s*((tanhP[0]*s+tanhP[1])*s+tanhP[2])/(((s+tanhQ[0])*s+tanhQ[1])*s+tanhQ[2])` は、`x + x^3 P(x)/Q(x)` の形式の有理関数近似を実装しています。`tanhP` と `tanhQ` の係数を用いて、Horner法に似た効率的な方法で多項式を評価しています。この近似は、`x` が `0` に近い範囲で非常に高い精度を提供します。
この新しい実装は、入力値の特性に応じて最適な数値計算戦略を適用することで、`math.Tanh`関数のパフォーマンスと精度を大幅に向上させています。
## 関連リンク
* Go言語 `math` パッケージのドキュメント: [https://pkg.go.dev/math](https://pkg.go.dev/math)
* `math.Tanh` 関数のドキュメント: [https://pkg.go.dev/math#Tanh](https://pkg.go.dev/math#Tanh)
* 双曲線関数 (Wikipedia): [https://ja.wikipedia.org/wiki/%E5%8F%8C%E6%9B%B2%E7%B7%9A%E9%96%A2%E6%95%B0](https://ja.wikipedia.org/wiki/%E5%8F%8C%E6%9B%B2%E7%B7%9A%E9%96%A2%E6%95%B0)
* 有理関数近似 (Wikipedia): [https://ja.wikipedia.org/wiki/%E6%9C%89%E7%90%86%E9%96%A2%E6%95%B0%E8%BF%91%E4%BC%BC](https://ja.wikipedia.org/wiki/%E6%9C%89%E7%90%86%E9%96%A2%E6%95%B0%E8%BF%91%E4%BC%BC)
## 参考にした情報源リンク
* Cephes Math Library (Netlib): [http://netlib.sandia.gov/cephes/](http://netlib.sandia.gov/cephes/)
* Cephes Math Library `cmath.tgz` (Netlib): [http://www.netlib.org/cephes/cmath.tgz](http://www.netlib.org/cephes/cmath.tgz)
* Cephes Math Library `tanh.c` (直接リンクは提供されていないが、`cmath.tgz`内に含まれる)# [インデックス 13843] ファイルの概要
このコミットは、Go言語の標準ライブラリ`src/pkg/math/tanh.go`ファイルに対する変更です。このファイルは、数値計算における双曲線正接(hyperbolic tangent, tanh)関数を実装しています。`tanh`関数は、シグモイド関数の一種であり、ニューラルネットワークの活性化関数や信号処理など、様々な科学技術計算で利用されます。
## コミット
このコミットは、`math.Tanh`関数の実装を改善し、パフォーマンスと精度を向上させることを目的としています。具体的には、既存の`Sinh`と`Cosh`関数を用いた実装から、Cephes Math Libraryのアルゴリズムに基づくより高速で高精度な有理関数近似へと変更されています。これにより、`Tanh`関数の計算速度が大幅に向上し、同時に精度もわずかに改善されました。
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/0f8f5d2140120d00fee53c633e82265d6d4e6784](https://github.com/golang/go/commit/0f8f5d2140120d00fee53c633e82265d6d4e6784)
## 元コミット内容
math: Faster Tanh
From 159 to 47.6 ns/op; slightly more accurate.
R=rsc, golang-dev, mtj, dave, remyoudompheng CC=golang-dev https://golang.org/cl/6500121
## 変更の背景
この変更の主な背景は、`math.Tanh`関数のパフォーマンスと精度の向上です。元の実装では、`Tanh(x)`を`Sinh(x) / Cosh(x)`として計算していました。しかし、このアプローチにはいくつかの課題がありました。
1. **パフォーマンス**: `Sinh`と`Cosh`関数はそれぞれ指数関数`Exp`を用いて計算されるため、`Tanh`の計算には複数の`Exp`呼び出しが必要となり、計算コストが高くなりがちでした。コミットメッセージにあるように、変更前は159 ns/opかかっていた計算が、変更後には47.6 ns/opへと大幅に短縮されています。これは約3.3倍の高速化に相当します。
2. **精度**: `Sinh(x) / Cosh(x)`という形式は、`x`が非常に大きい場合にオーバーフローやアンダーフローを引き起こす可能性があり、また、浮動小数点数の精度限界により、特定の範囲で誤差が蓄積しやすいという問題がありました。特に`x`が0に近い場合、`Sinh(x)`と`Cosh(x)`が非常に小さな値になるため、除算の精度が問題となることがあります。
3. **既存の数学ライブラリの活用**: 多くの高性能な数学ライブラリ(例えばCephes Math Library)では、`tanh`関数に対して、より効率的で高精度な数値計算アルゴリズムが採用されています。これらのアルゴリズムは、特定の範囲で有理関数近似を用いるなど、浮動小数点数の特性を考慮した最適化が施されています。このコミットは、そのような既存の知見を取り入れることで、Go言語の標準ライブラリの品質を向上させることを目指しました。
この変更により、`math.Tanh`関数はより高速かつ高精度になり、Go言語で数値計算を行うアプリケーションの全体的なパフォーマンスと信頼性の向上に貢献します。
## 前提知識の解説
### 双曲線正接関数 (Hyperbolic Tangent, tanh)
双曲線正接関数 `tanh(x)` は、数学における双曲線関数の一つで、以下のように定義されます。
`tanh(x) = sinh(x) / cosh(x)`
ここで、`sinh(x)` は双曲線正弦関数、`cosh(x)` は双曲線余弦関数であり、それぞれ以下のように定義されます。
`sinh(x) = (e^x - e^(-x)) / 2`
`cosh(x) = (e^x + e^(-x)) / 2`
これらの定義から、`tanh(x)` は以下のように書き換えることができます。
`tanh(x) = (e^x - e^(-x)) / (e^x + e^(-x))`
この関数は、`-1` から `1` の範囲の値を取り、`x` が大きくなるにつれて `1` に、`x` が小さくなるにつれて `-1` に漸近します。`x = 0` のとき `tanh(0) = 0` となります。そのS字型の形状から、ニューラルネットワークの活性化関数として広く利用されています。
### 有理関数近似 (Rational Function Approximation)
有理関数近似とは、ある関数を、2つの多項式の比(有理関数)で近似する方法です。
`R(x) = P(x) / Q(x)`
ここで `P(x)` と `Q(x)` は多項式です。有理関数近似は、テイラー級数展開などの多項式近似と比較して、より広い範囲で高い精度を達成できる場合があります。特に、関数の特異点付近や漸近挙動を持つ関数に対して有効です。
`tanh(x)` の場合、`x` が小さい範囲では `x + x^3 P(x)/Q(x)` の形式が用いられることがあります。これは、`tanh(x)` が `x` の周りで奇関数である(`tanh(-x) = -tanh(x)`)という性質を利用したもので、`x` の奇数乗のみを含む多項式で近似することで、計算効率と精度を両立させます。
### Cephes Math Library
Cephes Math Libraryは、Stephen L. Moshierによって開発された、C言語で書かれた高品質な数学関数ライブラリです。このライブラリは、様々な特殊関数や初等関数(三角関数、指数関数、対数関数など)の高精度な実装を提供しており、科学技術計算や数値解析の分野で広く利用されています。
Cephesライブラリの関数は、多くの場合、特定の入力範囲に対して異なるアルゴリズム(例えば、有理関数近似や多項式近似)を適用することで、計算効率と精度を最適化しています。このコミットで導入された`tanh`関数の実装も、Cephesライブラリの`tanh.c`を参考にしています。
## 技術的詳細
新しい`math.Tanh`関数の実装は、入力値 `x` の絶対値 `z = Abs(x)` に応じて、以下の3つのケースに分岐して計算を行います。
1. **`z > 0.5 * MAXLOG` の場合**:
* `MAXLOG` は `8.8029691931113054295988e+01` であり、これは `log(2^127)` に相当します。これは、`float64` の最大値に近い値であり、`exp(2*z)` がオーバーフローする可能性のある非常に大きな `x` の範囲をカバーします。
* この範囲では、`tanh(x)` は `x` が正であれば `1` に、負であれば `-1` に非常に近くなります。そのため、`x` の符号に応じて `1` または `-1` を直接返します。これにより、不必要な計算や浮動小数点数の精度限界による誤差を避けることができます。
2. **`z >= 0.625` の場合**:
* この範囲では、`tanh(x)` の定義式 `tanh(x) = 1 - 2 / (exp(2x) + 1)` を利用して計算します。
* まず `s = Exp(2 * z)` を計算します。
* 次に `z = 1 - 2 / (s + 1)` を計算します。
* 最後に、元の `x` の符号に応じて `z` の符号を調整します(`x < 0` ならば `z = -z`)。
* この式は、`x` が十分に大きい場合に `tanh(x)` が `1` に漸近するという性質を効率的に利用しています。`exp(2x)` が十分に大きくなると、`2 / (exp(2x) + 1)` は `0` に近づくため、`tanh(x)` は `1` に近づきます。
3. **上記以外のケース (つまり `z < 0.625`)**:
* この範囲は `x` が `0` に近い場合をカバーします。
* まず、`x == 0` の場合は `x` (つまり `0`) をそのまま返します。これは `tanh(0) = 0` であるため、正確な結果を返します。
* それ以外の場合、有理関数近似 `x + x*s*((tanhP[0]*s+tanhP[1])*s+tanhP[2])/(((s+tanhQ[0])*s+tanhQ[1])*s+tanhQ[2])` を使用して `tanh(x)` を計算します。
* ここで `s = x * x` です。これは、`tanh(x)` が奇関数であるため、`x` の偶数乗を効率的に計算するための準備です。
* `tanhP` と `tanhQ` は、それぞれ分子と分母の多項式の係数を格納する `float64` 型の配列です。
```go
var tanhP = [...]float64{
-9.64399179425052238628E-1,
-9.92877231001918586564E1,
-1.61468768441708447952E3,
}
var tanhQ = [...]float64{
1.12811678491632931402E2,
2.23548839060100448583E3,
4.84406305325125486048E3,
}
```
* これらの係数は、Cephes Math Libraryの`tanh.c`から引用されたもので、特定の精度要件を満たすように設計されています。この形式は、Horner法に似た効率的な多項式評価を可能にします。
この多分岐アプローチにより、`tanh`関数は入力値の範囲に応じて最適なアルゴリズムを選択し、全体として高速かつ高精度な計算を実現しています。
## コアとなるコードの変更箇所
```diff
--- a/src/pkg/math/tanh.go
+++ b/src/pkg/math/tanh.go
@@ -4,12 +4,66 @@
package math
-/*
- Floating-point hyperbolic tangent.
+// The original C code, the long comment, and the constants
+// below were from http://netlib.sandia.gov/cephes/cmath/sin.c,
+// available from http://www.netlib.org/cephes/cmath.tgz.
+// The go code is a simplified version of the original C.
+// tanh.c
+//
+// Hyperbolic tangent
+//
+// SYNOPSIS:
+//
+// double x, y, tanh();
+//
+// y = tanh( x );
+//
+// DESCRIPTION:
+//
+// Returns hyperbolic tangent of argument in the range MINLOG to MAXLOG.
+// MAXLOG = 8.8029691931113054295988e+01 = log(2**127)
+// MINLOG = -8.872283911167299960540e+01 = log(2**-128)
+//
+// A rational function is used for |x| < 0.625. The form
+// x + x**3 P(x)/Q(x) of Cody & Waite is employed.
+// Otherwise,
+// tanh(x) = sinh(x)/cosh(x) = 1 - 2/(exp(2x) + 1).
+//
+// ACCURACY:
+//
+// Relative error:
+// arithmetic domain # trials peak rms
+// IEEE -2,2 30000 2.5e-16 5.8e-17
+//
+// Cephes Math Library Release 2.8: June, 2000
+// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
+//
+// The readme file at http://netlib.sandia.gov/cephes/ says:
+// Some software in this archive may be from the book _Methods and
+// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
+// International, 1989) or from the Cephes Mathematical Library, a
+// commercial product. In either event, it is copyrighted by the author.
+// What you see here may be used freely but it comes with no support or
+// guarantee.
+//
+// The two known misprints in the book are repaired here in the
+// source listings for the gamma function and the incomplete beta
+// integral.
+//
+// Stephen L. Moshier
+// moshier@na-net.ornl.gov
+//
-\tSinh and Cosh are called except for large arguments, which\n-\twould cause overflow improperly.\n-*/\n+var tanhP = [...]float64{\n+\t-9.64399179425052238628E-1,\n+\t-9.92877231001918586564E1,\n+\t-1.61468768441708447952E3,\n+}\n+var tanhQ = [...]float64{\n+\t1.12811678491632931402E2,\n+\t2.23548839060100448583E3,\n+\t4.84406305325125486048E3,\n+}\n \n // Tanh computes the hyperbolic tangent of x.\n //
@@ -18,15 +72,26 @@ package math\n //\tTanh(±Inf) = ±1\n //\tTanh(NaN) = NaN\n func Tanh(x float64) float64 {\n-\tif x < 0 {\n-\t\tx = -x\n-\t\tif x > 21 {\n+\tconst MAXLOG = 8.8029691931113054295988e+01 // log(2**127)\n+\tz := Abs(x)\n+\tswitch {\n+\tcase z > 0.5*MAXLOG:\n+\t\tif x < 0 {\n \t\t\treturn -1\n \t\t}\n-\t\treturn -Sinh(x) / Cosh(x)\n-\t}\n-\tif x > 21 {\n \t\treturn 1\n+\tcase z >= 0.625:\n+\t\ts := Exp(2 * z)\n+\t\tz = 1 - 2/(s+1)\n+\t\tif x < 0 {\n+\t\t\tz = -z\n+\t\t}\n+\tdefault:\n+\t\tif x == 0 {\n+\t\t\treturn x\n+\t\t}\n+\t\ts := x * x\n+\t\tz = x + x*s*((tanhP[0]*s+tanhP[1])*s+tanhP[2])/(((s+tanhQ[0])*s+tanhQ[1])*s+tanhQ[2])\n \t}\n-\treturn Sinh(x) / Cosh(x)\n+\treturn z\n }\n```
## コアとなるコードの解説
変更された`Tanh`関数の実装は、以下の主要な部分から構成されます。
1. **定数と係数の定義**:
* `MAXLOG` 定数: `8.8029691931113054295988e+01` は、`log(2^127)` に相当し、`exp(2x)` がオーバーフローする可能性のある境界値として使用されます。
* `tanhP` と `tanhQ` 配列: これらは、`x` が小さい場合の有理関数近似で使用される多項式の係数です。これらの値は、Cephes Math Libraryから引用されており、高い精度を保証するために慎重に選択されています。
2. **入力値の絶対値の取得**:
* `z := Abs(x)`: 入力 `x` の絶対値 `z` を計算します。`tanh`関数は奇関数であるため、絶対値で計算し、最後に符号を調整するアプローチは一般的であり、効率的です。
3. **条件分岐によるアルゴリズム選択**:
* `switch` ステートメントを使用して、`z` の値に基づいて異なる計算パスを選択します。
* **ケース 1: `z > 0.5 * MAXLOG` (非常に大きな入力)**
```go
case z > 0.5*MAXLOG:
if x < 0 {
return -1
}
return 1
```
`x` の絶対値が非常に大きい場合、`tanh(x)` は `1` (x > 0) または `-1` (x < 0) に非常に近くなります。このケースでは、計算を省略し、直接 `1` または `-1` を返します。これにより、オーバーフローのリスクを回避し、最高のパフォーマンスと十分な精度を確保します。
* **ケース 2: `z >= 0.625` (中程度の入力)**
```go
case z >= 0.625:
s := Exp(2 * z)
z = 1 - 2/(s+1)
if x < 0 {
z = -z
}
```
`x` の絶対値が `0.625` 以上で、かつ非常に大きくない場合、`tanh(x) = 1 - 2 / (exp(2x) + 1)` の公式を使用します。`Exp`関数はGoの`math`パッケージで提供される指数関数です。計算された `z` は、最後に元の `x` の符号に基づいて調整されます。この方法は、`x` が `0` から離れるにつれて `tanh(x)` が `1` に漸近するという性質を効率的に利用します。
* **ケース 3: `default` (小さな入力)**
```go
default:
if x == 0 {
return x
}
s := x * x
z = x + x*s*((tanhP[0]*s+tanhP[1])*s+tanhP[2])/(((s+tanhQ[0])*s+tanhQ[1])*s+tanhQ[2])
```
`x` の絶対値が `0.625` 未満の場合、つまり `x` が `0` に近い場合、有理関数近似を使用します。
* `if x == 0 { return x }` は、`tanh(0) = 0` の特殊ケースを正確に処理します。
* `s := x * x` は、`x` の偶数乗を効率的に計算するための準備です。
* `z = x + x*s*((tanhP[0]*s+tanhP[1])*s+tanhP[2])/(((s+tanhQ[0])*s+tanhQ[1])*s+tanhQ[2])` は、`x + x^3 P(x)/Q(x)` の形式の有理関数近似を実装しています。`tanhP` と `tanhQ` の係数を用いて、Horner法に似た効率的な方法で多項式を評価しています。この近似は、`x` が `0` に近い範囲で非常に高い精度を提供します。
この新しい実装は、入力値の特性に応じて最適な数値計算戦略を適用することで、`math.Tanh`関数のパフォーマンスと精度を大幅に向上させています。
## 関連リンク
* Go言語 `math` パッケージのドキュメント: [https://pkg.go.dev/math](https://pkg.go.dev/math)
* `math.Tanh` 関数のドキュメント: [https://pkg.go.dev/math#Tanh](https://pkg.go.dev/math#Tanh)
* 双曲線関数 (Wikipedia): [https://ja.wikipedia.org/wiki/%E5%8F%8C%E6%9B%B2%E7%B7%9A%E9%96%A2%E6%95%B0](https://ja.wikipedia.org/wiki/%E5%8F%8C%E6%9B%B2%E7%B7%9A%E9%96%A2%E6%95%B0)
* 有理関数近似 (Wikipedia): [https://ja.wikipedia.org/wiki/%E6%9C%89%E7%90%86%E9%96%A2%E6%95%B0%E8%BF%91%E4%BC%BC](https://ja.wikipedia.org/wiki/%E6%9C%89%E7%90%86%E9%96%A2%E6%95%B0%E8%BF%91%E4%BC%BC)
## 参考にした情報源リンク
* Cephes Math Library (Netlib): [http://netlib.sandia.gov/cephes/](http://netlib.sandia.gov/cephes/)
* Cephes Math Library `cmath.tgz` (Netlib): [http://www.netlib.org/cephes/cmath.tgz](http://www.netlib.org/cephes/cmath.tgz)
* Cephes Math Library `tanh.c` (直接リンクは提供されていないが、`cmath.tgz`内に含まれる)