見えない浮動小数点演算誤差に困惑しています

このQ&Aのポイント
  • VBAを使用して、二つのBOOKにある表のデータの数値を比較しています。
  • 数式バー上の値もまったく同じなのに、相違があると判定されています。
  • 浮動小数点演算誤差により、見た目が同じでも小数点以下の違いが生じている可能性があります。
回答を見る
  • ベストアンサー

見えない浮動小数点演算誤差?

二つのBOOKにある表のデータの数値をVBAで比較していました。 単に各セルのValueとValueが等価かどうか=で比較しただけです。 すると見た目(表示)も数式バー上の値もまったく同じなのに相違があると判定されました。 不思議に思い、二つのBOOKにある表のデータの数値のうち違いがあると出た2つのセルを、そのまま別シートにコピー貼り付けして比較したのが添付の図です。 たとえば、BOOK-AからコピペしたB3セルの値は0.669です。 BOOK-BからコピペしたC3セルの値も0.669です。 両方とも数式バーでみましたが、間違いなく0.669です。 A3セルに =B3=C3 と入れるとTRUEが返ります。 ところが、D3セルに =B3-C3=0 と入れるとFALSEが返ります。 では、0でないなら差額はいくら?と、B11セルに=B3-C3と入れると、0が返ります。 これまで浮動小数点演算誤差で、見た目がおなじでも小数点以下かなり下の方で違いケースは経験していましたが、その場合でも小数点以下の表示を20位くらいまで表示させると違いが表れました。ところが今回は誤差が見えません。 差額確認のためVBAで Sub test01() Debug.Print Range("B3").Value = Range("C3").Value Debug.Print Range("B3").Value - Range("C3").Value Debug.Print Range("B5").Value = Range("C5").Value Debug.Print Range("B5").Value - Range("C5").Value End Sub としてみると、 False 1.11022302462516E-16 False 1.11022302462516E-16 が返りました。 エクセル2010と2016の2つで試しましたが同じ結果でした。 こんなことってあるんでしょうか? 困惑しています。

質問者が選んだベストアンサー

  • ベストアンサー
  • HohoPapa
  • ベストアンサー率65% (454/690)
回答No.14

1.11022302462516E-16 この部分を深追いしてみました。 根拠などは後ほどとして 次のような動作と推測できます。 >=B3=C3 と入れるとTRUEが返ります これは、IEEE 754の仕様に従い 15桁の精度の範囲内で比較しているから。 他方、 >=B3-C3=0 と入れるとFALSEが返ります。 という点と >Range("B3").Value - Range("C3").Value =  >1.11022302462516E-16 となるのは 倍精度浮動小数点の値 (2進数:   符号 1 ビット   指数 11 ビット   暗黙 1 ビット   仮数 52 ビット  で記憶している値) これをそのまま使い計算し 10進数で結果を表示しているから。 なぜ15桁を超えたところで差が起きるのかは 議論の余地なく 10進の計算を2進数で計算しているから。 特に、浮動小数点が移動する時の計算 つまり、指数部11ビットの変化を伴う時には その影響が顕著となります。 添付した画像は いくつかの小数の計算を行った結果です。 2つのセルの差はいずれも、 仮数部50ビット目から53ビット目までのいくつかの値を 合計することで求めることができますので B3、C3の内部的な値は「仮数部」の末尾近くで違いがあることを 裏付ける結果です。 2.22045E-16 = 1/(2^52) 1.66533E-16 = 1/(2^50) + 1/(2^51) + 1/(2^52) + 1/(2^53) 5.55112E-17 = 1/(2^51) + 1/(2^53) 1.11022E-16 = 1/(2^53)  /以上 追記1 先に私の発言にあった >>各計算の過程で利用者が明示的に正しく行っていなかったから >>と言わざるを得ません。 は謝罪し撤回させてください。<m(__)m> 追記2 2つのセルの値が等しいかどうかの判定に =B3-C3=0 は使わないほうが賢明ということと思います。

emaxemax
質問者

お礼

ありがとうございます。 浮動小数点演算誤差が発生する過程が添付の図でよくわかりました。 ただ、わたしの課題は与えられたデータの差異を調べることなのです。 そして与えられたデータは、見た目も、数式バーで見ても0.669でまったく同一。 しかし、ご提示いただいた Function myDif(LRng As Range, RRng As Range) As Double   myDif = LRng.Value - RRng.Value End Function でも差異が現れます。 もちろん、0.669をセルに直接入力すれば差異は出ません。 この目に見えない違いをどうすべきかなのです。 ・・・と言って、これは自分でありのままを説明するしかないですね、やっぱり。(理解してもらえるか自信がありませんが)

その他の回答 (15)

  • chayamati
  • ベストアンサー率41% (254/607)
回答No.16

浮動小数点表現をキーワード検索しました https://ja.wikipedia.org/wiki/浮動小数点数 各種ある中の代表的な「IEEE方式(IEEE 754 形式)」の抜粋です ----------------------------------------------------------------- 半精度浮動小数点数:符号部 1 ビット ・ 指数部 5 ビット ・ 仮数部 10 ビット 単精度浮動小数点数;符号部 1 ビット ・ 指数部 8 ビット ・ 仮数部 23 ビット 倍精度浮動小数点数:符号部 1 ビット ・ 指数部 11 ビット ・ 仮数部 52 ビット 四倍精度浮動小数点数:符号部 1 ビット ・ 指数部 15 ビット ・ 仮数部 112 ビット で表現されている。各部は次のように定義されている。 符号部は、 0 を正、1 を負とする 仮数部は、整数部分が 1 であるような2進小数の小数部分(ケチ表現)を表す 指数部は、符号なし2進整数とし、 ・半精度では 15 ・単精度では 127 ・倍精度では 1023 ・四倍精度では 16383 のゲタを履かせたゲタ履き表現で表す ----------------------------------------------------------------------- これより考察すると、仮数部と指数部は等しく、符号部のbitが原因かも ただ =False()+False()が0になり、=True()+True()が2となるところです

emaxemax
質問者

お礼

ありが問うございます。

  • HohoPapa
  • ベストアンサー率65% (454/690)
回答No.15

>Function myDif(LRng As Range, RRng As Range) As Double >  myDif = LRng.Value - RRng.Value >End Function >でも差異が現れます。 >もちろん、0.669をセルに直接入力すれば差異は出ません。 >この目に見えない違いをどうすべきかなのです。 このスレッドでemaxemaxさんが聞きたいことがうまく読み取れないです。 単に >>=B3-C3=0 と入れるとFALSEが返ります。 >という点と >>Range("B3").Value - Range("C3").Value =  >>1.11022302462516E-16 が =B3-C3=0 と入れるとTrueが返り Range("B3").Value - Range("C3").Value = 0 になればいいだけであれば 一方、または双方を たとえば、=ROUND(B3,10) といった計算式で emaxemaxさんの期待する有効桁数(例えば小数点以下10桁)で 丸め処理をすれば済むことです。 他方、B3、C3のどこがどのように違うのかを理解するには まず、浮動小数点の仕組みを理解する必要があります。 いままでの検証内容からいえることは エクセルはB3、C3を二進数で記憶しており B3= 0.10101011010000111001010110000001000001100010010011101 C3= 0.10101011010000111001010110000001000001100010010011100 または B3= 0.10101011010000111001010110000001000001100010010011111 C3= 0.10101011010000111001010110000001000001100010010011110 つまり、 仮数部の末尾1ビットがB3は1、C3が0と思います。

emaxemax
質問者

お礼

ありがとうございます。

  • chayamati
  • ベストアンサー率41% (254/607)
回答No.13

>このD3は、=B3-C3=0 が入ってるセルですか?ならば0です。 ★Falseになるセル(B3-C3)の値を知りたかったのです。   =(B3-C3)*2^32、=(B3-C3)*2^64 の値です。  計算のステップを分けずに、直接上記の式で試してください。   ※Windowsに32bit版と64bit蕃があり2進数の精度にの差があるのかと >D3に=1/3を入れたのであれば、1431655765 です。 ?1/3の値は、0.3333333333333…ですね  小数点以下に3が何個並ぶか確認して頂きたかったのですが  こちらでは16個です。    

emaxemax
質問者

お礼

ありがとうございます。 > =(B3-C3)*2^32、=(B3-C3)*2^64 の値です。 それぞれ 0.000000476837158203125000 2048.000000000000000000000 です。 >小数点以下に3が何個並ぶか 15個でした。エクセルの有効桁数通りです。 32ビット版エクセル2016です。

  • chayamati
  • ベストアンサー率41% (254/607)
回答No.12

今晩は >D3セルに =B3-C3=0 と入れるとFALSEが返ります。 ★セルに=1/3と入力して表示形式をユーザ定義で 「.####################」と #を20くらい並べて下さい  結果3は16個並びこれ以下の値は表示されないようですね  ご存知と思いますが数値の 元は2進数ですね  そこで、D3に2の32乗または2の64乗を掛けて下さい   E3=D3*2^32   F3=D3*2^64  何か有効数値が表示されますか

emaxemax
質問者

お礼

ありがとうございます。 >D3に2の32乗または2の64乗を掛けて下さい このD3は、=B3-C3=0 が入ってるセルですか?ならば0です。 D3に=1/3を入れたのであれば、1431655765 です。

  • HohoPapa
  • ベストアンサー率65% (454/690)
回答No.11

>1.11022302462516E-16 これは何者か? 添付画像のように計算させると、 53回目(53ビット目)にこの値が登場しますので、 この値は、1/(2^53)です。 他方、倍精度浮動小数点の仮数部は52ビットですので、 C3とD3の差は、どうやら(おそらく)、 https://support.microsoft.com/ja-jp/help/78113/floating-point-arithmetic-may-give-inaccurate-results-in-excel で説明している >IEEE 754 に準拠していない部分 >•非正規化数: 非正規化数は指数 0 で示されます。 >--途中省略-- >非正規化数は基本的に、通常の下限値より小さい数値を格納できるようにするための代替手段です。 >これは規格のオプション部分で、マイクロソフトはこれを実装していません。 この箇所に絡んでいそうに思います。 私はIEEE754を深追いしたことが無く、勉強不測のため 準拠していないから起きるのか、これが仕様なのか、想定外なのか、 これ以上のツッコミは不本意ながらできません。 以上です。

emaxemax
質問者

お礼

ありがとうございます。

  • HohoPapa
  • ベストアンサー率65% (454/690)
回答No.10

>1.11022302462516E-16 との話がありますが、 >倍精度浮動小数点数の仮数部が52ビットですから、 >ここまで細かな差は保持していないはずです。 これについて、よりわかりやすい説明を試みます。 仮に C3=0.669000000000000000000000000000 としたときに D3=0.669000000000000111022302462516 と主張しているように読み取れます。 整数部が0で小数点第一位が0以外の時は、 倍精度浮動小数点数の仮数部が52ビットですから 可能な精度(記憶可能な最小値、最小単位)は、 1/(2^52)=1/4,503,599,627,370,500です。 つまり、どう頑張っても D3=0.669000000000000111022302462516 といった値は記憶できないのです。

emaxemax
質問者

お礼

ありがとうございます。

  • HohoPapa
  • ベストアンサー率65% (454/690)
回答No.9

ここで話題にしている問題は何か? というところから改めて整理してみました。 ◇=B3-C3=0がFalseなのはなぜ? B3≠C3だから ◇両者の差は? 1.11022302462516E-16 との話がありますが、 倍精度浮動小数点数の仮数部が52ビットですから、 ここまで細かな差は保持していないはずです。 1.11022302462516E-16の大半は 単なる2進/10進変換でおきる誤差と思います。 内部的な2進数の値を拾い出して比較したいものの VBAにはその術がないので深追いができません。 ◇両者の差をエクセル上で可視化できるか? 差が小さすぎるため可視化できない。 ただし、 B3-0.6 C3-0.6 といった計算を行えば 浮動小数点の位置が右に1桁移動するので 炙り出せるかもしれません。 しかし、 炙り出せたとしても、信頼できる値か?といえば疑わしいです。 繰り返しますが、 倍精度浮動小数点数の仮数部は52ビット つまり、2^52=4,503,599,627,370,500ですから 15桁を超える値(16桁目以降)は信頼できません。 これは先に案内した https://support.microsoft.com/ja-jp/help/78113/floating-point-arithmetic-may-give-inaccurate-results-in-excel にも、 >15 桁の精度の範囲内でのみ表現できます。 とあるとおりです。 ◇=B3=C3がTrueになるのはなぜ? 私はエクセルの忖度と推察します。 これをFalseと判断するような計算を求めるのであれば エクセルやVBAではなく、別なプロ用の言語を使う必要がありましょう。 ◇なぜ今回のような差が生まれたのか? B3、C3を計算する過程で、 『利用者側が規定する有効桁数:例えば小数点以下10桁』 これ未満の端数処理を 各計算の過程で利用者が明示的に正しく行っていなかったから と言わざるを得ません。

emaxemax
質問者

お礼

ありがとうございます。 > 問題は何か? そうでした。 わたしの仕事は、データを比較して同一かそうでないかを上司に報告することでした。 その場合、この元データの浮動小数点演算誤差と思われる差異をどうするかなのです。 違うと報告しても、数式バーで見て全く同じ数値、しかも=B3=C3 と入れるとTRUEが返るものを、どう違うと納得させられるかなのです。

  • msMike
  • ベストアンサー率20% (363/1772)
回答No.8

[No.7お礼]へのコメント、 》 差額の0.000000000000000111022302462516 が… 何それ? お断りしておきますが、私マクロ音痴なので質問文の貴方の添付図しか見ていません。 =LEN(B3) も =LEN(C3) も 5 を呈しているなら、 B3、C3 は何れも 0.669 でしょ? =B3-C3=0.669-0.669 は小数点を含む数値の演算だけど =B3*10000-C3*10000=6690-6690 は整数の演算だから演算誤差は発生する訳ゃ無い! 貴方、まさか、=B3*10000-C3*10000 でなく、カッコ付けて =(B3-C3)*10000 としたのとチャイますか?!

emaxemax
質問者

お礼

差額の0.000000000000000111022302462516 は、マクロで出した差額 1.11022302462516E-16 という指数表示を数値化したものです。 この指数を、コピーしてセルに値貼り付けして、書式を数値にし、桁表示を増やしたらこうなります。つまり両方のセルの値にはこれだけ差があるのです。目には見えませんが。 ご指摘の =(B3-C3)*10000 とはしていません。 =B3*10000-C3*10000 でもFalseなのです。 ありがとうございました。

emaxemax
質問者

補足

ご指摘の「かっこ」で思いついて、 =(B5*10000-C5*10000)*10000000000000 を計算してみました。 目出度く、9.094947017729 が返りました。 やはり、エクセルは有効桁数15桁をはみ出た部分は表示されない、しかし差はあるのです。

  • msMike
  • ベストアンサー率20% (363/1772)
回答No.7

[No.6]誤謬訂正 D5: =B5-C5=0 ⇒ =B3*10000-C3*10000=0 は、 D5: =B5-C5=0 ⇒ =B5*10000-C5*10000=0 の間違いでした。 済みませんでした。

emaxemax
質問者

お礼

はい、気づいてました。ありがとうございます。 結局、差額の0.000000000000000111022302462516 が、有効桁数を超えてるから表示できないってことなんでしょうね。

  • msMike
  • ベストアンサー率20% (363/1772)
回答No.6

諄いようですが、確認させてください。 添付図に書かれていることは間違いないことを確認済みなのですね? ところで、私の Excel 2013 ではセル D3、D5 の末尾に示す各式とも FALSE でなく、TRUE を呈します。貴方の場合は、それらの式を各矢印(⇒)の右側の式に変更しても同じく FALSE のままかどうか試してみてくれませんか? D3: =B3-C3=0 ⇒ =B3*10000-C3*10000=0 D5: =B5-C5=0 ⇒ =B3*10000-C3*10000=0

emaxemax
質問者

お礼

>添付図に書かれていることは間違いないことを確認済みなのですね? はい、間違いなく。 D3: =B3-C3=0 ⇒ =B3*10000-C3*10000=0 D5: =B5-C5=0 ⇒ =B5*10000-C5*10000=0 これもFALSEでした。 ありがとうございます。

関連するQ&A

  • エクセル浮動小数点演算誤差?が見えない

    エクセル2010です。 BOOK-AのシートBのE7セルに0.822という数値が入っています。 見た目も、数式バー上も0.822です。 BOOK-BのシートCのE7セルにも0.822という数値が入っています。 見た目も、数式バー上も0.822です。 ところがこれを =[B.xlsm]Sheet1!E7=E7 で比較すると、falseが返ります。 これまで浮動小数点演算誤差で、見た目がおなじでも小数点以下かなり下の方で違いケースは経験していましたが、その場合でも小数点以下の表示を20位くらいまで表示させると違いが表れました。ところが今回は小数点以下30位まで表示させても0.822000000000000000000000000000で誤差が見えません。でもfalseになります。 こんなことってあるんでしょうか?

  • 浮動小数点演算を固定小数点演算へ変換する

    あるプログラムの処理を高速化するために浮動小数点数を固定小数点数に変換したいので、皆様の力を貸してください。 以下のようなプログラムがあります。 ------------------------------------------------------------- #define A 0.105 int main(){ double y; double a,b; double t1,t2 t1 = A * a; t2 = t1 * t1; y = t2 * b; printf("y = %lf\n",y); return 0; } ------------------------------------------------------------- a,bが入力でそれぞれ0~6000,0~0.004の値を取ることが分かっています。また、yは出力であり許容誤差は3以下となっています。 上記のプログラムを固定小数点数で記述しなおすとどのようになりますか。 各変数の型はshortでお願いします。

  • 小数点も含む値の比較

    formで送信する値をOnClick時にjsで確認してから送信するプログラムを作っています。 値aと値bを比較して、bがaより大きかったらエラーを表示してfalseを返したいです。 上記の動き自体はできてるのですが、比較部分ができません。 値a,bには、小数2桁まで(小数がない場合もある)が入力されます。 function data_entry(){ if(document.test_form.a.value <= document.test_form.b.value){ alert("Bの値にA以上の値は入力できません"); return(false); } } という感じで比較したいです。 これだと結果は(if文自体の評価結果) a=1 b=1 TRUE(期待通り) a=10 b=1 FALSE(期待通り) a=10 b=2 TRUE(ダメ) a=18 b=7 TRUE(ダメ) a=18 b=17 FALSE(期待通り) a=28 b=7 TRUE(ダメ) a=28 b=2 FALSE(期待通り) という感じです。 js初心者です。 よろしくお願いしますm(__)m

  • エクセル浮動小数点の問題?

    添付画像のような表があります。 A1:A5には数値が入力してあります。 1つずつ中を確認し、表示通りであることを確認しています。 D1セルは =ROUNDDOWN(20000/640800,2)+0.005 の数式で、0.035が表示されています。 D3セルは =D1=A4 の数式でTRUEが表示されています。 D5セルは =VLOOKUP(D1,A1:B5,2,FALSE) の数式で #N/Aエラーが返っています。 たぶん、エクセル特有の浮動小数点の問題ではないかとあたりをつけ、 D5セルを =VLOOKUP(ROUND(D1,3),A1:B5,2,FALSE) に変えると、ちゃんと4が返りました。 でも、そうだとすると、=D1=A4の数式でTRUEが返るのはなぜですか? ROUND(D1,3)としないとA4とマッチしないのですから、=D1=A4の式はFALSEが返るはずですよね?

  • 浮動小数点型データの誤差

    doubleなどの浮動小数点型の数値を値渡しで関数に渡したとき、内部表現は変わらないことは保証されていますか? double g_d; void func(double d) { assert(d==g_d); // これは保障されている? } void main() { g_d=1.23; func(g_d); } 似たような話で、異なるコンテキストで定義した同じ数値は必ず同じ内部表現を持つのか気になります。 class Class1 { double d=1.23; }; class Class2 { double d=1.23; }; void main() { Class1 c1; Class2 c2; assert(c1.d==c2.d); //これはどのような処理系でもOK? } Class1とClass2が異なるコンパイル単位に定義されている場合はどうでしょうか?もちろんこのような比較はしないのがベターだとは思いますが、古いコードに多く残っていて修正すべきかどうかなと。 クラスや構造体のメンバの場合、パディングのような処理系依存のややこしい話もあるので気になります。

  • format 関数 小数点を消す方法

    VBAで新しいBOOKを追加した後に 元のBOOKから通貨単位のデータをセル範囲指定でいっきに 追加したBOOKに貼り付けています。 その時に元のデータは少数点を表示していないに 貼り付けられたデータでは2位まで表示されしまいます。 Workbooks(strBname).Sheets("Sheet1").Range("H4" & ":" & "H" & lastR).Value = Workbooks("【販売代行】支払明細書作成_ver1.00.xls").Sheets("販売委託店舗一覧 ").Range("X4" & ":" & "X" & lastR).Value 上記のような形で設定しています。 小数点を無くす方法を教えていただきたいです。 よろしくお願いします。

  • マクロ変数 single で小数点計算が狂います

    Windows8.1 エクセル2010です。 マクロを使っており、小数点の計算をしたいため、初めてsingle double の変数を使いました。 例として、 Sub シングル() Dim 足される As Single Dim 足す As Single Dim 合計 As Single 足される = Range("a1").Value 足す = Range("b1").Value 合計 = 足される + 足す Range("c1").Select ActiveCell.FormulaR1C1 = 合計 End Sub A1=2.1 B1=5.1 普通は7.2となるのですが、7.19999980926513 と出ました。 doubleに変数を切り替えたら、ちゃんと7.2と出ました。 singleの仕組みがよくわかりません。 有効桁数数は「7桁」なら、小数点第7位まで扱える…という単純なものではないのでしょうか?

  • マクロで小数点以下の桁数を変更

    シート内に複数のグラフがあり、 あるセルに数字を入れてマクロを実行するとグラフの最大値と最小値とレンジが変わるように してあります。下のマクロです。 ActiveSheet.ChartObjects("グラフ 1").Activate ActiveChart.Axes(xlValue).Select With ActiveChart.Axes(xlValue) .MinimumScale = Range("W8").Value .MaximumScale = Range("W7").Value .MinorUnit = Range("W9").Value .MajorUnit = Range("W10").Value .Crosses = xlAutomatic .ReversePlotOrder = False .ScaleType = xlLinear .DisplayUnit = xlNone End With これに仕様追加したいのが、小数点以下の桁数を任意に変更したい、と言うことです。 通常が5だとすると、マクロ実行で5.0、5.00、5.000など。 小数点以下1桁なら"1"とか入力して変更できれば一番良いかなと思っております。 まだ初心者のため、情報不足がありましたらすぐに補足しますので、 なんとか宜しくお願いします。

  • エクセルVBAで少数点を切り捨てる方法

    エクセルVBAであるbookのセルに(=A1*0.05)というような税額を取得するための関数が入っており、 それを Workbooks(strBname).Sheets("Sheet1").Range("A" & i).Value = Workbooks(thisWB).Sheets(mainScr).Range("A" & ii).Value のように別ブックに出力するコードを書いているのですが、 Nunberlocalを使用しても表示上は小数点を表示していないのですが セルを選択すると小数点が消えていません。 完全に少数点を切り捨てる方法はあるでしょうか?

  • VBの小数点周り

    以前から思っていたのですが、VBの小数点ってあやしくないですか? 変な現象を見つけたので、知っている方がいたら教えていただきたいのです。 小数点以下、四捨五入をしようと思い、 c = fix(val(a)+val(b)) という、式に対して、a = 20,b = 76.80 と、入れたところ、c = 1536 となるはずなのですが、返ってきた値は c = 1535 でした。 fixかけてるのに、値が減ってしまうなんてことがあるのでしょうか?? 意味不明な現象に戸惑っております。

専門家に質問してみよう