[解決済み] [解答】なぜこれらの数字は等しくないのですか?
質問
次のコードは明らかに間違っています。 何が問題なのでしょうか?
i <- 0.1
i <- i + 0.05
i
## [1] 0.15
if(i==0.15) cat("i equals 0.15") else cat("i does not equal 0.15")
## i does not equal 0.15
解決方法は?
一般的な(言語にとらわれない)理由
すべての数字を正確に表現できるわけではありませんので IEEE浮動小数点演算 (ほとんどすべてのコンピュータが十進数を表現し、それを使って計算するために使用している規格)であるため、必ずしも期待通りの結果が得られるとは限りません。特に、単純な有限の小数である値(0.1や0.05など)の中には、コンピュータで正確に表現できないものがあるため、それに対する演算の結果、"known"の答えを直接表現したのと同じ結果が出ない場合があるからです。
これはコンピュータの演算の限界としてよく知られていることで、いくつかの場所で議論されている。
- R FAQに質問があります。 R FAQ 7.31
- R Inferno by Patrick Burns はこの問題に最初のサークルを割いています(9ページ目から)。
- David Goldberg, "What Every Computer Scientist Should Know About Floating-point Arithmetic, ".「すべてのコンピュータ科学者が浮動小数点演算について知っておくべきこと」; ACMコンピューティングサーベイス 23 , 1 (1991-03), 5-48 doi>10.1145/103162.103163 ( 改訂版もあります )
- 浮動小数点演算ガイド - 浮動小数点演算についてプログラマが知っておくべきこと
- 0.30000000000000004.com プログラミング言語間の浮動小数点演算の比較
-
Stack Overflowの質問をいくつか紹介します。
- 浮動小数点数はなぜ不正確なのですか?
- なぜ10進数は2進数で正確に表現できないのですか?
- 浮動小数点演算は壊れているのか?
- floating point is inaccurate".の正準複製。 (この問題の正規の回答に関するメタ・ディスカッション)
スカラーを比較する
での標準的な解答は
R
を使用しないことです。
==
を使用するのではなく
all.equal
関数を使用します。というか
all.equal
は、もし違いがあれば、その違いを詳細に説明します。
isTRUE(all.equal(...))
.
if(isTRUE(all.equal(i,0.15))) cat("i equals 0.15") else cat("i does not equal 0.15")
イールド
i equals 0.15
を使用するいくつかの例を挙げます。
all.equal
の代わりに
==
(最後の例は、これで差分が正しく表示されることを示すためのものです)。
0.1+0.05==0.15
#[1] FALSE
isTRUE(all.equal(0.1+0.05, 0.15))
#[1] TRUE
1-0.1-0.1-0.1==0.7
#[1] FALSE
isTRUE(all.equal(1-0.1-0.1-0.1, 0.7))
#[1] TRUE
0.3/0.1 == 3
#[1] FALSE
isTRUE(all.equal(0.3/0.1, 3))
#[1] TRUE
0.1+0.1==0.15
#[1] FALSE
isTRUE(all.equal(0.1+0.1, 0.15))
#[1] FALSE
をそのままコピーしたものです。 同様の質問に対する回答 :
あなたが遭遇した問題は、浮動小数点はほとんどの場合、小数の分数を正確に表すことができないので、完全一致に失敗することが頻繁に起こるということです。
と言いながら、Rはわずかに嘘をつく。
1.1-0.2
#[1] 0.9
0.9
#[1] 0.9
10進数で本音を知ることができる。
sprintf("%.54f",1.1-0.2)
#[1] "0.900000000000000133226762955018784850835800170898437500"
sprintf("%.54f",0.9)
#[1] "0.900000000000000022204460492503130808472633361816406250"
これらの数字が異なることはおわかりいただけると思いますが、ちょっと扱いにくい表現になっています。 2進数で見ると(16進数でも同じですが)、より明確なイメージが得られます。
sprintf("%a",0.9)
#[1] "0x1.ccccccccccccdp-1"
sprintf("%a",1.1-0.2)
#[1] "0x1.ccccccccccccep-1"
sprintf("%a",1.1-0.2-0.9)
#[1] "0x1p-53"
で異なることがわかります。
2^-53
この数値は、このように値が1に近い2つの数値の間で表現できる最小の差であるため、重要である。
この表現可能な最小の数が何であるかは、任意のコンピュータに対して、Rの マシン フィールドがあります。
?.Machine
#....
#double.eps the smallest positive floating-point number x
#such that 1 + x != 1. It equals base^ulp.digits if either
#base is 2 or rounding is 0; otherwise, it is
#(base^ulp.digits) / 2. Normally 2.220446e-16.
#....
.Machine$double.eps
#[1] 2.220446e-16
sprintf("%a",.Machine$double.eps)
#[1] "0x1p-52"
この事実を利用して、差が浮動小数点数で表現できる最小の数に近いかどうかをチェックする「ほぼ等しい」関数を作成することができます。 実はこれはすでに存在している。
all.equal
.
?all.equal
#....
#all.equal(x,y) is a utility to compare R objects x and y testing ‘near equality’.
#....
#all.equal(target, current,
# tolerance = .Machine$double.eps ^ 0.5,
# scale = NULL, check.attributes = TRUE, ...)
#....
つまり、all.equal関数は、実際には数値の差が2つのマンティサの最小差の平方根であることをチェックしているわけです。
このアルゴリズムは、デノーマルと呼ばれる極端に小さい数の近くでは少しおかしなことになりますが、その心配はありません。
ベクトルの比較
上記の議論は、2つの単一の値の比較を想定しています。R では、スカラーは存在せず、ベクトルだけが存在し、暗黙のベクトル化がこの言語の強みです。ベクトルを要素ごとに比較する場合、前の原則は維持されますが、実装は若干異なります。
==
はベクトル化されている(要素ごとの比較を行う)のに対し
all.equal
はベクトル全体を1つの実体として比較します。
先ほどの例でいうと
a <- c(0.1+0.05, 1-0.1-0.1-0.1, 0.3/0.1, 0.1+0.1)
b <- c(0.15, 0.7, 3, 0.15)
==
は、期待された結果を得られず、また
all.equal
は要素単位で実行されません
a==b
#[1] FALSE FALSE FALSE FALSE
all.equal(a,b)
#[1] "Mean relative difference: 0.01234568"
isTRUE(all.equal(a,b))
#[1] FALSE
むしろ、2つのベクトルをループするバージョンを使用する必要があります。
mapply(function(x, y) {isTRUE(all.equal(x, y))}, a, b)
#[1] TRUE TRUE TRUE FALSE
これの機能的なバージョンが必要な場合は、次のように書くことができます。
elementwise.all.equal <- Vectorize(function(x, y) {isTRUE(all.equal(x, y))})
として呼び出すことができます。
elementwise.all.equal(a, b)
#[1] TRUE TRUE TRUE FALSE
または、ラップする代わりに
all.equal
の関連する内部構造を複製するだけで、より多くの関数呼び出しが可能になります。
all.equal.numeric
で、暗黙のベクトル化を使用します。
tolerance = .Machine$double.eps^0.5
# this is the default tolerance used in all.equal,
# but you can pick a different tolerance to match your needs
abs(a - b) < tolerance
#[1] TRUE TRUE TRUE FALSE
このようなアプローチで
dplyr::near
であり、それ自体が
これは、2つの浮動小数点数のベクトルが(対で)等しいかどうかを比較する安全な方法です。これは
==
許容範囲が設定されているため
dplyr::near(a, b)
#[1] TRUE TRUE TRUE FALSE
関連
-
[解決済み] なぜGCCはa*a*a*a*aを(a*a*a)*(a*a*a)に最適化しないのでしょうか?
-
[解決済み] 0.1fを0にすると、なぜ10倍もパフォーマンスが落ちるのですか?
-
[解決済み] 通貨を表すのにDoubleやFloatを使ってはいけないのですか?
-
[解決済み] 10進数のrange()ステップ値を使用するには?
-
[解決済み] Rの代入演算子"="と"<-"の違いは何ですか?
-
[解決済み] 関数のソースコードを見るにはどうしたらいいですか?
-
[解決済み] 非常に大きなテーブルをデータフレームとして高速に読み込む
-
[解決済み] javascriptで小数点以下2桁の浮動小数点を解析する方法は?
-
[解決済み] Rでtrycatchの書き方
-
[解決済み】なぜ10進数は2進数で正確に表現できないのですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] promise already under evaluation: recursive default argument reference or earlier problems?
-
[解決済み】添え字付き代入でNAを使用することはできません
-
[解決済み】Rで「パッケージ'FILE_PATH'のインストールで終了ステータスが0でなかった」。
-
[解決済み】LMEモデルのレベル0、ブロック1でのバックソルブにおける特異性
-
[解決済み】apply()とadply()の出力が異なる件)
-
[解決済み】RでAIC中に行数が変化するのはなぜですか?そうならないようにするにはどうしたらいいですか?
-
[解決済み】Rヒストグラムの範囲エラー:いくつかの'x'がカウントされない; 'break'が'x'の範囲に及ばないかもしれない
-
[解決済み】Rでデータフレームのサブセットをプロットする方法は?
-
[解決済み] [Solved] read.csv warning 'EOF within quoted string' prevents complete reading of file.
-
[解決済み】R4DSのエラー比較(1)は、アトミック型とリスト型でのみ可能です