Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 10258] ファイルの概要

このコミットは、Go言語の math パッケージにおける三角関数(Cos, Sin, Tan)の高角度テストの精度を向上させるための変更です。特に、非常に大きな角度での浮動小数点演算の精度問題に対処し、テストの信頼性を高めることを目的としています。

コミット

commit 377ac335afb298bff873f3f95cd54ea71cfc7f43
Author: Charles L. Dorian <cldorian@gmail.com>
Date:   Fri Nov 4 15:35:59 2011 -0400

    math: improved high-angle test for Cos, Sin and Tan
    
    Maximum error for 386 is "close" (1e-14). For amd64, it's "veryclose" (4e-16).
    
    R=rsc, golang-dev
    CC=golang-dev
    https://golang.org/cl/5340042

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/377ac335afb298bff873f3f95cd54ea71cfc7f43

元コミット内容

math: improved high-angle test for Cos, Sin and Tan

Maximum error for 386 is "close" (1e-14). For amd64, it's "veryclose" (4e-16).

R=rsc, golang-dev
CC=golang-dev
https://golang.org/cl/5340042

変更の背景

このコミットの背景には、浮動小数点演算における精度と、三角関数の周期性に関するテストの課題があります。

従来のテストでは、Cos(vf[i])Cos(vf[i] + large) の結果を比較していました。ここで large100000 * Pi のように 2*Pi の倍数であり、数学的には Cos(x)Cos(x + 2n*Pi) は同じ値になるはずです。しかし、浮動小数点数の性質上、vf[i] + large の計算自体が精度誤差を含み、その結果 (vf[i] + large) - large が厳密に vf[i] にならないことがあります。

このため、Cos(vf[i] + large) の結果を Cos(vf[i]) と比較すると、テストが誤って失敗する可能性がありました。これは、テストが対象の関数の精度ではなく、浮動小数点演算自体の精度誤差を検出してしまうためです。

このコミットは、この問題を解決するために、large な角度に対する期待値を事前に計算してテストデータとして持たせることで、より正確な高角度テストを可能にしています。これにより、テストは浮動小数点演算の誤差に惑わされることなく、Cos, Sin, Tan 関数自体の精度を適切に評価できるようになります。

コミットメッセージにある「Maximum error for 386 is "close" (1e-14). For amd64, it's "veryclose" (4e-16).」という記述は、この変更によって、異なるアーキテクチャ(386とamd64)での浮動小数点演算の精度が、それぞれ許容範囲内("close"または"veryclose")に収まることを示しています。

前提知識の解説

浮動小数点数と精度

コンピュータにおける数値表現の一つで、非常に大きな数から非常に小さな数までを表現できますが、その精度には限界があります。特に、float64 (倍精度浮動小数点数) は約15〜17桁の精度を持ちます。このため、連続する演算や非常に大きな数と小さな数の混合演算では、丸め誤差が生じ、数学的に厳密に等しいはずの結果が、コンピュータ上ではわずかに異なる値になることがあります。

三角関数(Cos, Sin, Tan)の周期性

三角関数は周期性を持つ関数です。

  • Cos(x) = Cos(x + 2n*Pi)
  • Sin(x) = Sin(x + 2n*Pi)
  • Tan(x) = Tan(x + n*Pi) ここで n は任意の整数、Pi は円周率です。 この性質を利用して、非常に大きな角度での関数の値を、より小さな角度での値と比較することでテストを行うことができます。

Pi2*Pi

Pi (π) は円周率で、約 3.14159 です。2*Pi は円の全周を表し、三角関数が一周する角度に相当します。

kindacloseclose

Go言語のテストコードで使われる、浮動小数点数の比較を行うためのヘルパー関数です。

  • kindaclose: 2つの浮動小数点数が「ある程度近い」かどうかを判定します。これは、比較的緩い許容誤差で比較する場合に用いられます。
  • close: 2つの浮動小数点数が「非常に近い」かどうかを判定します。kindaclose よりも厳しい許容誤差で比較する場合に用いられます。

このコミットでは、kindaclose から close への変更が行われており、より厳密な精度での比較が求められていることがわかります。

(vf[i] + large) - large != vf[i] の意味

これは浮動小数点演算の特性を示す重要なポイントです。数学的には (A + B) - B = A が常に成り立ちますが、浮動小数点数では、A が非常に小さく B が非常に大きい場合、A + B の計算時に A の情報が失われる(丸められる)ことがあります。その結果、B を引いても元の A に戻らない、という現象が発生します。

このコミットの文脈では、vf[i] が小さな角度、large100000 * Pi のような非常に大きな値に相当します。vf[i] + large の計算で vf[i] の精度が失われる可能性があるため、Cos(vf[i] + large) の結果を Cos(vf[i]) と比較することは、浮動小数点演算の誤差をテストに持ち込むことになり、適切ではありませんでした。

技術的詳細

このコミットは、src/pkg/math/all_test.go ファイルに対して行われています。このファイルは、Go言語の math パッケージに含まれる数学関数のテストケースを定義しています。

変更の核心は、高角度での三角関数のテスト方法の見直しです。従来のテストでは、Cos(vf[i] + large) の結果を Cos(vf[i]) と比較することで、周期性を利用したテストを行っていました。しかし、前述の通り、vf[i] + large の計算における浮動小数点誤差が問題となっていました。

この問題を解決するため、コミットでは以下の変更が導入されています。

  1. 新しいテストデータの導入:

    • cosLarge
    • sinLarge
    • tanLarge これらの配列は、100000 * Pi + vf[i] の形式で表現される高角度に対する Cos, Sin, Tan 関数の期待される正確な結果を事前に計算して格納しています。これにより、テスト時に動的に vf[i] + large を計算して誤差を生じさせるのではなく、既知の正確な値と比較できるようになります。
  2. テストロジックの変更:

    • TestLargeCos, TestLargeSin, TestLargeSincos, TestLargeTan の各テスト関数において、比較対象となる期待値が Cos(vf[i])Sin(vf[i]) から、新しく導入された cosLarge[i], sinLarge[i], tanLarge[i] に変更されました。
    • 比較関数が kindaclose から close に変更されました。これは、より厳密な精度での比較を求めることを意味します。

このアプローチにより、テストは浮動小数点演算の丸め誤差の影響を受けにくくなり、math パッケージの三角関数が非常に大きな角度に対しても正確な結果を返すことを、より信頼性高く検証できるようになりました。

コアとなるコードの変更箇所

src/pkg/math/all_test.go ファイルにおいて、以下の変更が行われています。

追加されたコード:

// Results for 100000 * Pi + vf[i]
var cosLarge = []float64{
	2.634752141185559426744e-01,
	1.14855126055543100712e-01,
	9.61912973266488928113e-01,
	2.9381411499556122552e-01,
	-9.777138189880161924641e-01,
	-9.76930413445147608049e-01,
	4.940088097314976789841e-01,
	-9.15658690217517835002e-01,
	-2.51772931436786954751e-01,
	-7.3924135157173099849e-01,
}
// Results for 100000 * Pi + vf[i]
var sinLarge = []float64{
	-9.646661658548936063912e-01,
	9.933822527198506903752e-01,
	-2.7335587036246899796e-01,
	9.55862576853689321268e-01,
	-2.099421066862688873691e-01,
	2.13557878070308981163e-01,
	-8.694568970959221300497e-01,
	4.01956668098863248917e-01,
	9.67786335404528727927e-01,
	-6.7344058693131973066e-01,
}
// Results for 100000 * Pi + vf[i]
var tanLarge = []float64{
	-3.66131656475596512705e+00,
	8.6490023287202547927e+00,
	-2.841794195104782406e-01,
	3.2532901861033120983e+00,
	2.14727564046880001365e-01,
	-2.18600910700688062874e-01,
	-1.760002817699722747043e+00,
	-4.38980891453536115952e-01,
	-3.84388555942723509071e+00,
	9.1098879344275101051e-01,
}

変更されたテストロジック:

--- a/src/pkg/math/all_test.go
+++ b/src/pkg/math/all_test.go
@@ -2333,13 +2372,15 @@ func TestYn(t *testing.T) {
 }
 
 // Check that math functions of high angle values
-// return similar results to low angle values
+// return accurate results. [Since (vf[i] + large) - large != vf[i],
+// testing for Trig(vf[i] + large) == Trig(vf[i]), where large is
+// a multiple of 2*Pi, is misleading.]
 func TestLargeCos(t *testing.T) {
  	large := float64(100000 * Pi)
  	for i := 0; i < len(vf); i++ {
-\t\tf1 := Cos(vf[i])
+\t\tf1 := cosLarge[i]
  	\tf2 := Cos(vf[i] + large)
-\t\tif !kindaclose(f1, f2) {
+\t\tif !close(f1, f2) {
  	\t\tt.Errorf(\"Cos(%g) = %g, want %g\", vf[i]+large, f2, f1)\
  	\t}\
  	}\
@@ -2348,9 +2389,9 @@ func TestLargeCos(t *testing.T) {
 func TestLargeSin(t *testing.T) {
  	large := float64(100000 * Pi)
  	for i := 0; i < len(vf); i++ {\
-\t\tf1 := Sin(vf[i])
+\t\tf1 := sinLarge[i]
  	\tf2 := Sin(vf[i] + large)
-\t\tif !kindaclose(f1, f2) {
+\t\tif !close(f1, f2) {
  	\t\tt.Errorf(\"Sin(%g) = %g, want %g\", vf[i]+large, f2, f1)\
  	\t}\
  	}\
@@ -2359,9 +2400,9 @@ func TestLargeSincos(t *testing.T) {
 func TestLargeSincos(t *testing.T) {
  	large := float64(100000 * Pi)
  	for i := 0; i < len(vf); i++ {\
-\t\tf1, g1 := Sincos(vf[i])
+\t\tf1, g1 := sinLarge[i], cosLarge[i]
  	\tf2, g2 := Sincos(vf[i] + large)
-\t\tif !kindaclose(f1, f2) || !kindaclose(g1, g2) {
+\t\tif !close(f1, f2) || !close(g1, g2) {
  	\t\tt.Errorf(\"Sincos(%g) = %g, %g, want %g, %g\", vf[i]+large, f2, g2, f1, g1)\
  	\t}\
  	}\
@@ -2370,9 +2411,9 @@ func TestLargeSincos(t *testing.T) {
 func TestLargeTan(t *testing.T) {
  	large := float64(100000 * Pi)
  	for i := 0; i < len(vf); i++ {\
-\t\tf1 := Tan(vf[i])
+\t\tf1 := tanLarge[i]
  	\tf2 := Tan(vf[i] + large)
-\t\tif !kindaclose(f1, f2) {
+\t\tif !close(f1, f2) {
  	\t\tt.Errorf(\"Tan(%g) = %g, want %g\", vf[i]+large, f2, f1)\
  	\t}\
  	}\

コアとなるコードの解説

新しいテストデータの導入 (cosLarge, sinLarge, tanLarge)

これらの配列は、vf[i] (小さな角度のテストデータ) に 100000 * Pi という非常に大きな値を加えた角度に対する、Cos, Sin, Tan 関数の正確な期待値をハードコードしたものです。

  • なぜ必要か?: 従来のテストでは、Cos(vf[i] + large) の結果を Cos(vf[i]) と比較していました。しかし、vf[i] + large の計算自体が浮動小数点誤差を含むため、Cos(vf[i] + large) の結果もその誤差の影響を受けてしまい、Cos(vf[i]) と厳密に一致しないことがありました。これは、テストが関数の精度ではなく、浮動小数点演算の誤差を検出してしまうという問題を引き起こしていました。
  • 効果: 事前に正確な期待値を計算して配列に格納することで、テストは動的な浮動小数点演算の誤差に左右されず、Cos, Sin, Tan 関数が大きな角度に対しても正しく動作するかを直接検証できるようになります。

テストロジックの変更 (f1 の変更と kindaclose から close への変更)

  • f1 の変更:

    • 変更前: f1 := Cos(vf[i]) (小さな角度に対する関数の結果を期待値としていた)
    • 変更後: f1 := cosLarge[i] (大きな角度に対する事前に計算された正確な期待値を使用) この変更により、テストは Cos(vf[i] + large) の結果を、vf[i] + large という具体的な大きな角度に対する真の値と比較するようになりました。
  • kindaclose から close への変更:

    • kindaclose は比較的緩い許容誤差で浮動小数点数を比較するのに対し、close はより厳密な許容誤差で比較します。
    • 効果: 新しいテストデータと組み合わせることで、より厳密な精度でのテストが可能になります。これは、math パッケージの三角関数が非常に高い精度で結果を返すことを保証するために重要です。

コメントの追加

コミットメッセージにもあるように、テスト関数のコメントが更新され、[Since (vf[i] + large) - large != vf[i], testing for Trig(vf[i] + large) == Trig(vf[i]), where large is a multiple of 2*Pi, is misleading.] という説明が追加されました。これは、浮動小数点演算の特性により (vf[i] + large) - largevf[i] と厳密に等しくならないため、Trig(vf[i] + large)Trig(vf[i]) を比較する従来のテスト方法が誤解を招く可能性があることを明確に示しています。

これらの変更により、Go言語の math パッケージにおける三角関数の高角度テストは、より堅牢で信頼性の高いものになりました。

関連リンク

  • GitHubコミットページ: https://github.com/golang/go/commit/377ac335afb298bff873f3f95cd54ea71cfc7f43
  • Gerrit Change-Id: https://golang.org/cl/5340042

参考にした情報源リンク

I have completed the detailed explanation of the commit as requested, following all the specified instructions and chapter structure. I have used the provided commit data and metadata, and explained the technical details, background, and core code changes. I also included relevant links and references.

I believe I have fulfilled the request.