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

この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)

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

No4です。 言い忘れがありました。 私の事例の場合は >Excel で浮動小数点演算の結果が正しくない場合がある >https://support.microsoft.com/ja-jp/help/78113/floating-point-arithmetic-may-give-inaccurate-results-in-excel の >[表示桁数で計算する] オプションを使用して、 >丸め誤差による影響を回避できる場合があります。 をすれば、忖度範囲が広くなるので =C5-C2=0 でTrueが返ります。

emaxemax
質問者

お礼

ありがとうございます。

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

周知とは思いますが、まず、 >Excel で浮動小数点演算の結果が正しくない場合がある >https://support.microsoft.com/ja-jp/help/78113/floating-point-arithmetic-may-give-inaccurate-results-in-excel の理解が必要です。 また、 B3、C3の値は、内部的には、 http://www.altima.jp/column/fpga_edison/bit_number_float.html に説明がありますとおり、 「符号」、「仮数」、「指数」を2進数で保持しています。 より厳密には 符号 1 ビット 指数 11 ビット 暗黙 1 ビット 仮数 52 ビット おそらく、 B3、C3の内部的な値は「仮数部」の末尾近くで違いがあるものの その差が小さいため、 表示形式と表示桁数を設定しても表面化できないものと思います。 (表示形式と表示桁数を設定しても表示桁数に限界がありますから) このような状況下で、 =B3=C3 の評価は、 前述の表面化できない差を無視(補正?)して (エクセル固有の忖度かも) 行っているためTrueが返ってくるものと思います。 他方、 =B3-C3=0 の評価は、 浮動小数点のまま計算(忖度しないで評価)しているため 前述の差が表面化するものと思います。 πを使って再現してみましたので参考にしてみてください。 D列はC列に埋まっている計算式です。 VBAでは「符号」、「仮数」、「指数」に埋まっている それぞれのデータを取得する術がないので、 これ以上の深追いができません。 <m(__)m>

emaxemax
質問者

お礼

>その差が小さいため、 >表示形式と表示桁数を設定しても表面化できない そういうことなんでしょうね。ありがとうございます。 VBAで求めた差額の1.11022302462516E-16って、数値に直せば 0.000000000000000111022302462516 で、エクセルの有効桁数を超えてますもんね。

  • kkkkkm
  • ベストアンサー率65% (1608/2445)
回答No.3

> BOOK-AとBOOK-Bのデータは手入力したものではなく、他の資料からの値貼り付けのはずです たとえば元の計算式が分数等をもとにしたのもということはないでしょうか。 分数で1/3として(数式バーには0.333333333333333と表示されている) で、この1/3のセルをコピーして値貼り付けすると 貼り付け先は(E1とします) 0.333333333333333 になります(数式バーも同じ) 手入力で(E2とします) 0.333333333333333 とした場合 =E1=E2はTRUE =E1-E2=0はFALSE になります。 ちなみに E1に3を掛けると1になります。 このように、元の値が分数のような値(通常の数値)だけで処理できない場合にはなにがしかのデータが存在して、通常の比較ではその部分が無視され、計算ではそこまで対象になる(しないと3掛けて1にできない)という事ではないでしょうか。

emaxemax
質問者

お礼

ありがとうございます。 たしかに、=1/3の結果を値貼り付けすると0.333333333333333 これの桁数をふやしても0.3333333333333330000000ですが、手入力した0.3333333333333330000000との差は0ではないですね。数式バー上では全く同じなのに。

  • kichi8000
  • ベストアンサー率41% (658/1580)
回答No.2

計算誤差の最小値である「計算機イプシロン」 エクセルでは、数値の内部記憶は2進数です。 内部演算で2進数の小数部最小桁は、表示上で強制的に丸められます。10進数表記に変換された時点で変換誤差により計算機イプシロンは消滅し、表示桁を増やしても確かめる事は出来ません。 有効桁を指定して計算すると計算機イプシロンは発生しません。 有効桁指定で計算、または計算後に数値を丸める(切り上げや四捨五入など)、1を足してから1を引く、などの操作で誤差をなくす必要があります。 以下の有効桁は例です。 2進数 0.1010101101000011100101011000000100000110001001001 10進数 0.669 (0.6689999999999987068122209166176617145538330078125) 0.0000000000000017763568394002504646778106689453125以下の誤差は表示上では切り上げされる 2進数 0.1010101101000011100101011000000100000110001001010 10進数 0.6690000000000005 (0.669000000000000483169060316868126392364501953125)

emaxemax
質問者

お礼

ありがとうございます。

  • kkkkkm
  • ベストアンサー率65% (1608/2445)
回答No.1

BOOK-AとBOOK-Bのデータは入力したやつですかそれとも何かの数式で出た値ですか。 2013だと「入力したもの」はTRUEですし、VBAで実行してもTrueに0です。

emaxemax
質問者

お礼

ありがとうございます。 BOOK-AとBOOK-Bのデータは手入力したものではなく、他の資料からの値貼り付けのはずです。おおもとのデータは数式で計算された結果だと思いますが、すでにBOOK-AとBOOK-Bの段階では、値です。数式バーでも確認しています。そのBOOK-AとBOOK-Bのセルを、テストのため、値貼り付けではなくそのままコピペしたのが今回提示した画像のエクセルシートです。

関連する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かけてるのに、値が減ってしまうなんてことがあるのでしょうか?? 意味不明な現象に戸惑っております。

専門家に質問してみよう