「.NETのfloat.Epsilon定数は計算機イプシロンではない」の版間の差分
ナビゲーションに移動
検索に移動
imported>Administrator (→解決) |
imported>Administrator |
||
(同じ利用者による、間の7版が非表示) | |||
12行目: | 12行目: | ||
== 解決 == | == 解決 == | ||
− | + | 「奥村晴彦『C言語による最新アルゴリズム事典』技術評論社,1991年,ISBN4-87408-414-1,2400円」という書籍に[[計算機イプシロン]]を動的に求める[[アルゴリズム]]が詳細に解説されており、また[[ソースコード]]は公式サイトにおいても配布されている。この本は非常に面白いので迷わず買え。個人的にはこの手の技術書を買う際に目次と技術評論社という名前だけで信頼して買うようになった一冊である。 | |
+ | https://oku.edu.mie-u.ac.jp/~okumura/algo/ | ||
+ | |||
+ | 速攻で[[C言語]]からC#に[[移植]]した(ほぼ[[コピペ]]しただけ)。 | ||
+ | |||
+ | <syntaxhighlight lang="csharp"> | ||
+ | using System; | ||
+ | |||
+ | class MainClass | ||
+ | { | ||
+ | static float Foo(float x) { return x; } | ||
+ | |||
+ | static float MachineEpsilon() | ||
+ | { | ||
+ | int b, p; | ||
+ | float x, y, eps; | ||
+ | |||
+ | x = y = 2; | ||
+ | while (Foo(x + 1) - x == 1) x *= 2; | ||
+ | while (Foo(x + y) == x) y *= 2; | ||
+ | b = (int)(Foo(x + y) - x); | ||
+ | p = 1; x = b; | ||
+ | while (Foo(x + 1) - x == 1) { p++; x *= b; } | ||
+ | eps = 1; | ||
+ | while (Foo(1 + eps / 2) > 1) eps /= 2; | ||
+ | eps = Foo(1 + eps) - 1; | ||
+ | |||
+ | return eps; | ||
+ | } | ||
+ | |||
+ | public static void Main(string[] args) | ||
+ | { | ||
+ | Console.WriteLine("epsilon = {0}", MachineEpsilon()); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | 計算機イプシロンの定番の[[アルゴリズム]]といえば、変数をひたすら2で割り続け、ゼロになった瞬間のひとつ前を捉えるというもの。「1以上」という条件はコンピューターの指数と仮数の組み合わせで表現される浮動小数点数では、1未満だと指数部だけを使ってどこまでも小さくなってしまうためであり、なんとしても仮数部を使おうということらしい。 | ||
+ | <syntaxhighlight lang="csharp"> | ||
static double MachineEpsilon() | static double MachineEpsilon() | ||
{ | { | ||
− | double eps = 1. | + | double eps = 1.0d; |
do | do | ||
{ | { | ||
− | eps /= 2. | + | eps /= 2.0d; |
} | } | ||
while ((double)(1.0 + eps) != 1.0); | while ((double)(1.0 + eps) != 1.0); | ||
+ | |||
return eps; | return eps; | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
+ | |||
+ | == 備考 == | ||
+ | 実行環境(主に[[CPU]]の違い)により計算結果が変化する点に注意すること。 | ||
+ | [[アーキテクチャ]]がいっぱいある[[Xamarin]]系は要注意。 | ||
[[category: .NET]] | [[category: .NET]] | ||
[[category: Mono]] | [[category: Mono]] | ||
[[category: Xamarin]] | [[category: Xamarin]] |
2018年8月14日 (火) 02:46時点における最新版
C#においてfloat型(Single型)の絶対値をfloat.Epsilon定数と比較している箇所の挙動がどうもおかしい。
原因[編集 | ソースを編集]
The value of the F:System.Single.Epsilon property is not equivalent to machine epsilon, which represents the upper bound of the relative error due to rounding in floating-point arithmetic. https://msdn.microsoft.com/ja-jp/library/system.single.epsilon(v=vs.110).aspx
要約すると「浮動小数点演算の丸の相対誤差の上限(丸め誤差発生時にズレるであろう最大値)」であり「計算機イプシロン」ではない。
まじかよ。名前紛らわしすぎだろ。
解決[編集 | ソースを編集]
「奥村晴彦『C言語による最新アルゴリズム事典』技術評論社,1991年,ISBN4-87408-414-1,2400円」という書籍に計算機イプシロンを動的に求めるアルゴリズムが詳細に解説されており、またソースコードは公式サイトにおいても配布されている。この本は非常に面白いので迷わず買え。個人的にはこの手の技術書を買う際に目次と技術評論社という名前だけで信頼して買うようになった一冊である。
https://oku.edu.mie-u.ac.jp/~okumura/algo/
using System;
class MainClass
{
static float Foo(float x) { return x; }
static float MachineEpsilon()
{
int b, p;
float x, y, eps;
x = y = 2;
while (Foo(x + 1) - x == 1) x *= 2;
while (Foo(x + y) == x) y *= 2;
b = (int)(Foo(x + y) - x);
p = 1; x = b;
while (Foo(x + 1) - x == 1) { p++; x *= b; }
eps = 1;
while (Foo(1 + eps / 2) > 1) eps /= 2;
eps = Foo(1 + eps) - 1;
return eps;
}
public static void Main(string[] args)
{
Console.WriteLine("epsilon = {0}", MachineEpsilon());
}
}
計算機イプシロンの定番のアルゴリズムといえば、変数をひたすら2で割り続け、ゼロになった瞬間のひとつ前を捉えるというもの。「1以上」という条件はコンピューターの指数と仮数の組み合わせで表現される浮動小数点数では、1未満だと指数部だけを使ってどこまでも小さくなってしまうためであり、なんとしても仮数部を使おうということらしい。
static double MachineEpsilon()
{
double eps = 1.0d;
do
{
eps /= 2.0d;
}
while ((double)(1.0 + eps) != 1.0);
return eps;
}
備考[編集 | ソースを編集]
実行環境(主にCPUの違い)により計算結果が変化する点に注意すること。 アーキテクチャがいっぱいあるXamarin系は要注意。