Script事業案内エクセル以外ホームマクロVBAマクロVBA技術解説VBAの少数以下の演算誤差について最終更新日:2020-01-29VBAの少数以下の演算誤差についてワークシートで時間計算をしていると、時に不可解な結果に見舞われることがあります。エクセルの時間は、1日を1とした数値で管理されているため、1時間は1の24分の1の数値になります。この少数以下の計算において、エクセルが正しく計算できないというものです。エクセルに限らず、一般的にコンピューターでは少数の計算を正しく行う事が出来ません。時間計算で困ったときの確実な対処方法Excelで非常に多い質問の一つが時間計算です、日付・時刻はエクセルのデータ形式の一つですが、特に時間計算は面倒で対処方法も書籍・ネットでもあまりみかけません。日付に関しては便利な関数も用意されていますし、なにより、1日が数値の1(これをシリアル値と言います)で表されているので、大抵はなんとかなっているようです。今回は、VBAで少数以下を扱ったときに発生する演算誤差について、どのようなものかの紹介と解決方法を解説します。少数以下の演算誤差とはコンピューターの中は2進数です。2進数は、桁の数値が2になると桁上がりするものです。1桁上がるごとに2倍されていきます。少数以下はどうなるかと言うと、1桁下がるごとに1/2になっていきます。0.000000001この表をみるとなんとなくわかるかと思いますが、表の中間の10進数値を2進数で正確に表すことはできません。10進数の0.1は、0.00011001100110011・・・このような2進数になってしまいます。VBAでは、少数を扱えるようにするために浮動小数点数が使われます。変数宣言のデータ型については、以下を参照してください。第12回.変数宣言のDimとデータ型|VBA入門マクロVBA入門者が、まず最初につまずくのが、このDimで変数を宣言することでしょう。良く説明されますが、まずはこの考えで良いでしょう。この入れ物には、いろいろなタイプの入れ物があります。前回は変数を説明しましたが、変数があれば、当然の定数もありConstで宣言します。変数は値が変わるもの、定数は値が変わらないものです。定数とは、数値や文字列の代わりに使用される、意味を持たせた名前のことです。)は8バイト、違いは精度の違いです。0.1のような数値は正確な少数として表現できません、どこかで桁落ちしてしまいます。これにより、正確な少数を扱えていないのでVBAでは誤差が発生してしまいます。ウインドウで、?0.1*3=0.3これはFalseと出力されます。この他、少数の四則演算を適当な数値でいろいろ確認してみてください。TrueになったりFalseになるのが確認できると思います。ウインドウに、FalseTrueTrueTrueTrueFalseTrueFalseTrueFalseと出力されている数値の組み合わせがあり、演算誤差が確認できます。これですと確認しづらいですし、VBAをいろいろ変更したいので以下のようなVBAで話を進めます。Sub実行すると、アクティブシートに以下のように出力されます。先のVBAでは、データ型を一切指定してませんでした。このVBAにデータ型を指定して演算誤差に対応していこうという趣旨になります。VBAでは、何も指定せずに少数を記述した場合はDoubleになります。まではInteger、これを超えるとLongの扱いになります。オーバーフローとなります。このようにLongの型文字&を付けるかCLng関数を使ってください。)まずはVBA既定のDoubleでやってみます。TRUEあれれ、と言う感じですよね。そもそも、0.1そのものが正しくセルに入っていません。最後の1行が出力されていません。Forで0.9までにしているので、最後が0.9をわずかに超えてしまっているという事です。0.1等の数値定数にSingle型の型宣言文字を指定して、0.1!全ての数値をこのようにしても結果は同じです。Singleは、Doubleに比べて精度が落ちる分、かえって演算誤差が出づらい傾向があります。上記の通り演算誤差が完全になくなるわけではありません。Singleを使えば演算誤差が出ないといった乱暴な意見を見かけることもありますが決してそのようなことはありません。?0.4!-0.3!イミディエイトで簡単に確認できます。Currency(通貨型)Currencyは少数4桁の固定小数点数です。Currencyは、整数形式の64ビット(8バイト)数値として保存されます。10,000でスケーリングされて、小数点の左側に15桁、右側に4桁の固定小数点数です。少数が4桁以内であれば、演算誤差は出ません。TRUEご覧の通り、通貨型なので書式設定されていないセルに出力すると通貨記号が付いてしまいますが、演算誤差は発生しません。少数が4桁までなら演算誤差を気にする必要がありません。Decimal(10進型)Decimalは、スケーリング係数(少数桁数)と正負を示す値とともに、96ビット(12バイト)の符号なし整数として保存されます。DimのAsで宣言できるデータ型にはありませんが、VBAではDecimal型があります。変数に入れる場合は、変数の型はVariantで宣言します。TRUE何も問題ありません。Decimalが扱える桁数範囲内であれば演算誤差を気にする必要はありません。Decimalのデータ範囲については、CDec関数をを参照してください。CDec関数は、引数をDecimal型(10進型)に変換します。引数expressionには任意の文字列式または数式を指定します。この引数は必ず指定します。少数以下の演算誤差にデータ型以外で対応するCurrency(通貨型)で扱えない少数以下5桁の場合で説明します。整数に直して計算そもそも少数が無ければ演算誤差は出ないのですから、整数にして計算すれば良いのです。TRUEここでは少数5桁なので100000倍して100000で割っています。この結果は当然問題ありません。少数桁数が多いと整数がLongに入りきれなくなる場合が出てきます。64bitエクセルならLongLongを使えば良いでしょう。これは結果として、Currencyの内部処理を自力でやっていることと同じだと考えれば良いでしょう。有効桁数で丸めるそもそも通常扱う少数の桁数はある程度きまっていることが多いはずです。少数の桁数が決まっているのなら、その桁数に丸めてしまえば良いのです。演算誤差は、ずっと小さい少数で発生しているので、必要な桁数に丸めてしまえば誤差は無くなります。TRUE少数の桁数は、Roundの第二引数で調整できます。実際に使う場合は、Roundする少数桁数を必要な桁数より少し多めに指定すると良いでしょう。VBAの少数以下の演算誤差の最後に何通りかのVBAを紹介しましたが、実際に使う場合にどれが良いかはその時々で変わってくると思います。少数以下4桁までならCurrencyが簡単かつ確実だと言えるでしょう。Decimalはより精度が高くなりますが、型宣言できないため扱いづらい面があります。整数にしたりRoundする方法は、より柔軟ではありますがVBAの記述が面倒になります。したがって、適宜使い分けると良いでしょう。WEB上ではVBA以外の例として消費税計算の例が存在しますが、VBAでは、税込算出/税抜算出において(余程特種な数式ではない限り)演算誤差が出ることは無いはずです。コンピューターというものは便利なようでつまらないところで気を使わなければならない代物ですね。Next)|VBA入門8.セルに文字を入れるとは(Range,Value)|VBA入門9.空白セルを正しく判定する方法(IsEmpty,IsError,HasFormula)|VBA技術解説10.ひらがな⇔カタカナの変換|エクセル基本操作ホームマクロVBAマクロVBA技術解説VBAの少数以下の演算誤差についてこのサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。GASスプレッドシート入門スプレッドシート応用GAS入門GAS応用ホームマクロVBAマクロVBA技術解説VBAの少数以下の演算誤差についてこのサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。