日経PC21 - 日経BP社 ビジネスマンのパソコン誌

このページの本文に進む

日経PC21サイト内リンクへ進む

PC Online
ホーム
ニュース
モバイル&スマートフォン
PC&Mac
DIY PC
周辺機器&カメラ
セキュリティ
クラウド
ソフトウエア
ビギナーズ
日経BP社 パソコン情報
日経トレンディネット
日経パソコン
ITpro

エクセル(Excel)の便利な使いこなし方法を解説するWEBスペシャル

全460ページの「エクセル(Excel)大事典」を読めば“演算誤差の謎”がよくわかる!

“達人”芳坂和行氏に学ぶ、エクセル(Excel)「演算誤差」対策講座

第4回 演算誤差の正体

―― IEEE 754 浮動小数点数の仕組み

この「演算誤差」対策講座も、いよいよ最終回です。そこで、演算誤差が発生する根本的な仕組みと、エクセルVBAを利用した誤差対策法について学びます。

エクセルが誤差を起こすそもそもの原因は、「IEEE(アイ・トリプル・イー)754」という数値計算の標準規格にあります。エクセルは、この規格に基づいて計算をしているのですが、この「IEEE 754」こそが、演算誤差の原因なのです。この辺りの仕組みを詳しく解説しましょう。

一般のユーザーにはちょっと難しい話になってしまいますが、理解できなくてもご安心ください。第3回までに学んだ演算誤差に対する知識と、対策法を押さえておけば、通常の問題はおおかた解決できます。今回は、演算誤差の仕組みについてより詳しく勉強したい方、エクセルVBAなどで誤差対策をしたい方に、ぜひ読んでいただきたいと思います。

「IEEE 754 規格」とは何か?

ハル
「先輩、おはようございます!」
エリカ
「ハルちゃん、おはよう! 昨日はエクセルの研修を受けたんだよね? どうだった?」
ハル
「はい。とても勉強になりました。講師は相沢部長だったんですよ」
エリカ
「相沢部長? どうして営業の部長がエクセルを教えてるの?」
ハル
「最近、講師の資格を取ったそうです。一度やってみたかった、とおっしゃっていました」
エリカ
「ふーん… あの部長じゃ、研修って言うよりテレビショッピングみたいになっちゃいそうね」
ハル
「あ、そうです。そんな感じでした。でも楽しかったですよ」
エリカ
「私も見たかったな!」
ハル
「誤差の勉強もしました。ただ、小数点以下の数に誤差があることはわかったんですが、そもそもどうして誤差があるのか、詳しい理由まではわかりませんでした」
エリカ
「それじゃ、今日は暇だから、もう少し詳しく教えてあげようか?」
ハル
「お、お願いします!」
エリカ
「IEEE(アイ・トリプル・イー)754 って聞いたことがある?」
ハル
「アイ・トリプル・イー… って何ですか?」
エリカ
「IEEE は米国電気電子技術者協会(The Institute of Electrical and Electronics Engineers)のこと。アメリカに本部があって世界中に会員がいる大きな学会なの。コンピュータ関係の規格もたくさん作っていて、IEEE が作った規格が国際規格に採用されることも多いんだ」
ハル
「754 というのは?」
エリカ
「IEEE の中のワーキンググループの番号で、規格の番号でもあるの。IEEE 754 は浮動小数点演算の規格。実はこの IEEE 754 規格がエクセルの演算誤差の原因なの」
エクセルは高速に計算するためにIEEE754の倍精度浮動小数点形式を使っている→誤差が発生する
ハル
「あの… 浮動小数点演算って何ですか?」
エリカ
「浮動小数点演算というのは、指数形式の数値を使う計算のこと。エクセルでも表示形式の中に『指数』というのがあって、例えば『1230』は『1.23E+03』って表示されるよね。これは『1.23×10の3乗』という意味。こういう指数形式で表現した数値のことを浮動小数点数(floating-point number) って言うの」
ハル
「浮動小数点というのは指数形式のことなんですね」
エリカ
「『1.23E+03』は見た目の小数点の位置と本当の小数点の位置が違っているよね。小数点位置が動くから浮動小数点って言うわけ」
ハル
「なるほど」
エリカ
「浮動小数点の反対は固定小数点。例えば『123.1』のようにそのままの小数点位置で表現したり、位置を移動するとしても必ず一定の桁数だけ移動するの」
ハル
「固定小数点の方がわかりやすいですよね。どうして浮動小数点を使うんですか?」
エリカ
「浮動小数点の方が少ない桁で広い範囲の数を表現できるの。例えば、1000兆分の1を表すには、固定小数点では『0.000000000000001』になって、たくさんの桁が必要だけど、浮動小数点なら『1.0E-15』と書くだけで済む。コンピューターでは桁数が少ない方が速く計算できるから、広い範囲の数を高速に計算するには固定小数点より浮動小数点の方が都合がいいの」
ハル
「そうなんですか」
固定小数点と浮動小数点の違い

なぜ規格が必要なのか?

エリカ
「『浮動小数点演算の規格』と聞いても、ピンと来ないと思うから、どうして規格が必要なのか説明するね。コンピューターはどんなデータも 0 と 1 の組み合わせだけで記録しているってことは知っているよね?」
ハル
「はい。聞いたことはあります。1 桁のことを『ビット』って言うんですよね。8 桁、つまり 8 ビットは『1 バイト』です」
エリカ
「うん。それじゃ『1000001010100000』っていうビットの並びはどういう意味かわかる?」
ハル
「…わかりません。どういう意味なんですか?」
エリカ
「私にもわからない。実は、コンピューターにもわからないんだ」
ハル
「え? どういうことですか?」
エリカ
「ビットの並びの意味は使う人が自分で決めていいことなの。『1000001010100000』は 33440 という数のことかもしれないし、-32096 という数のことかもしれない。41090 とか -24446 かもしれないし、文字の『あ』かもしれない。もっと他の意味かもしれない。使う人次第ってこと」
ビットの並びだけではデータの意味はわからない
ハル
「そうなんですか… でも、意味が決まっていなかったら、私が作ったデータを先輩が見ても何が書いてあるのかわかりませんよね? 困りませんか?」
エリカ
「そう。困るよね。だからデータにも規格があるの。規格に沿ったデータであれば、誰が見ても意味がわかるでしょう?」
ハル
「なるほど、そうですね」
エリカ
「実際、昔は浮動小数点数のデータ形式はコンピューターによってバラバラだったの。データをやりとりするときは変換処理が必要で、とても不便だった。そこでコンピューターの技術者が集まって共通のデータ形式と計算方法を決めようってことになったの。そしてできたのが IEEE 754 規格というわけ」
ハル
「そうだったんですか」
エリカ
「今ではほとんどのパソコンに IEEE 754 規格の演算装置が組み込まれている。この装置のことを浮動小数点演算装置とか、FPU(Floating-Point Unit)って言うの。広い範囲の数値を高速に計算できるから、ほとんどのソフトウェアはこの演算装置を使って数値計算をしている。エクセルも同じで、ワークシートの数値データはすべて IEEE 754 のデータ形式になっているの」
ハル
「IEEE 754 はとても重要な『裏方さん』なんですね」

IEEE 754 浮動小数点形式の概要

エリカ
「それじゃ、これから IEEE 754 でどんなふうにデータを表現するのか説明して行くね。ちょっと複雑な話になるけど、知らなくてもエクセルを使う上では困らないから、細かい内容は聞き流していいよ」
ハル
「わかりました」
エリカ
「まず、用語を憶えてね。浮動小数点数では『-1.23×10の3乗』のような形で数を表現するんだけど、先頭の『-』を符号、『1.23』を仮数(かすう)、『10』を基数(きすう)、『3』を指数(しすう)って言うの。つまり『(符号)(仮数)×(基数)の(指数)乗』で数を表現するわけ」
ハル
「符号、仮数、基数、指数ですね」
浮動小数点数の各部の名前 符号 仮数 基数 指数
エリカ
「IEEE 754 では基数は 10 ではなく 2 を使うの。つまり、必ず『(符号)(仮数)×2の(指数)乗』という形にする。データの形式には、単精度と倍精度、それからそれぞれの拡張精度の 4 種類があるの」
IEEE754の浮動小数点形式
ハル
「データ形式が 4 種類もあるんですか…」
エリカ
「とりあえず、単精度と倍精度が理解できればいいと思うよ。拡張精度は、単精度や倍精度の数を計算するときに内部的に使う形式だからユーザーが詳しく知る必要はないの」
ハル
「わかりました。単精度と倍精度ですね」
エリカ
「単精度と倍精度は、指数と仮数のビット数が違うだけで、仕組みはまったく同じ」
ハル
「指数と仮数のビット数が違うとどうなるんですか?」
エリカ
「表現できる数値の範囲が違ってくるの。例えば、仮数が『1.234567』のように10進数で7桁までなら単精度でも表現できる。でも、『1.2345678』のように8桁になると、単精度では表現できなくて、倍精度を使う必要があるの」
ハル
「単精度と倍精度では表現できる桁数や大きさが違うわけですね」
単精度と倍精度の表現できる範囲の違い

2進数の考え方

ハル
「具体的に指数部や仮数部にはどんなデータが入るんですか?」
エリカ
「それを理解するには2進数の知識が必要になるんだ。指数部も仮数部も2進数で表現するからね」
ハル
「2進数ですか…」
エリカ
「2進数について簡単に説明するね。私たちが普段使っているのは10進数。10進数では『0』から『9』までの 10 個の数字を使って、同じ桁の数が 10 個集まったら桁を繰り上げるよね」
ハル
「はい」
エリカ
「2進数では『0』と『1』だけを使って、2 になったら桁を繰り上げるの。『0』の次は『1』で、『1』の次は繰り上げて『10』 と書く」
2進数の数え方
ハル
「2進数の『10』は 10 ではなくて 2 のことなんですね… ちょっとややこしいですね」
エリカ
「次に小数点以下の数を考えてみるね」
ハル
「2進数にも小数点以下の数があるんですか?」
エリカ
「うん。あるよ。10進数の『0.1』『0.01』『0.001』はそれぞれ『10分の1』『100分の1』『1000分の1』という意味だよね。つまり、『10の累乗分の1』ということ」
ハル
「はい」
エリカ
「2進数の『0.1』『0.01』『0.001』は『2分の1』『4分の1』『8分の1』という意味になるの。つまり『2の累乗分の1』」
ハル
「『2分の1』『4分の1』『8分の1』ということは『0.5』『0.25』『0.125』ですね」
小数点以下の2進数
エリカ
「それじゃ、10進数を2進数に変換する方法を『14.75』という数を例にして説明するね。こんな図を考えてみて」
14.75を2進数で表現するときの考え方
ハル
「『14.75』は2進数では『1110.11』になるってことですか?」
エリカ
「そう。計算方法は、2進数の各桁を上から順番に引いて行くの。引けたらその桁は『1』、引けないなら『0』になるわけ。引いた残りが 0 になったら終わり」
ハル
「なるほど」
エリカ
「反対に2進数を10進数にするのは簡単だよね。2進数の『1110.11』は『8+4+2+0+0.5+0.25』という意味だから『14.75』になる」
ハル
「確かにそうなりますね」
エリカ
「お金をイメージするとわかりやすいよ」
ハル
「お金ですか?」
エリカ
「私たちは 1円玉、10円玉、100円玉のように『10 の累乗』のコインを使うよね。それから、実際には1円未満のコインはないけど、もしあったとしたら、0.1円玉、0.01円玉、0.001円玉のように『10の累乗分の1』のコインを使うはずだよね」
ハル
「そうですね」
エリカ
「2進数の国では 1円玉、2円玉、4円玉、8円玉、16円玉、32円玉のように『2 の累乗』のコインを使うの。1円未満のコインは、0.5円玉、0.25円玉、0.125円玉のように『2の累乗分の1』のコインを使う」
ハル
「0.5円玉ですか。おもしろいですね」
エリカ
「14.75円の買い物をしたら、8円玉1枚、4円玉1枚、2円玉1枚、1円玉0枚、0.5円玉1枚、0.25円玉1枚で支払うわけ。各コインの枚数を並べて書くと『1110.11』になる」
2進数のコインで14.75円を支払う
ハル
「なるほど。なんとなくイメージがわいてきました」
エリカ
「あと、もう一つだけ、桁をシフトする方法を知っておいてね。10進数では 10 を掛けたり割ったりすると桁をシフトできるけれど、2進数の場合は 2 を掛けたり割ったりするの。例えば、2進数の『1.0』は 2 を掛ければ『10.0』になるし、2 で割れば『0.10』になる。小数点位置を動かすときに使う計算だから憶えておいてね」
ハル
「わかりました」

倍精度形式の作り方

エリカ
「それじゃ、実際に浮動小数点数を作ってみようか。エクセルでは倍精度形式を使っているから、同じように倍精度形式で作ってみるね」
ハル
「倍精度というと、指数部が11ビット、仮数部が52ビットの方ですね」
エリカ
「うん。最初だからわかりやすいように『2.5』という数でやってみるね。まず、10進数を2進数に変換するの」
ハル
「『2.5』は『2円玉+0.5円玉』だから、2進数にすると『10.1』ですね」
エリカ
「そう。次に、基数が2の浮動小数点にするの。2進数の『10.1』は『10.1×2の0乗』と表現できるよね」
ハル
「『2の0乗』は『1』だから、そうなりますね」
エリカ
「でも、これだけではだめで、IEEE 754 では仮数は1以上2未満にそろえるという決まりがあるの。だから『10.1×2の0乗』を『1.01×2の1乗』に変える。『10.1』を2で割って『1.01』にして、割った分、『2の0乗』に2を掛けて『2の1乗』にするわけ」
ハル
「仮数を2で割って、指数の方を2倍しているから数は変わらないってことですね」
エリカ
「そう。こんなふうに桁を揃えることを、浮動小数点数の正規化って言うんだ」
ハル
「正規化ですね」
2.5を倍精度形式にする
エリカ
「これでデータが準備できたから、符号部から順番に入れて行くね。符号部はプラスなら『0』マイナスなら『1』にするの。今回はプラスの数だから『0』にする」
ハル
「符号部は簡単ですね」
エリカ
「次は指数部。実際の指数に 1023 を足してそれを2進数にするの。1023 はバイアス値と言って、マイナスの指数をプラスの数にするために足すんだ。『1.01×2の1乗』の指数は『1』。これに1023を足すと『1024』。『1024』はちょうど 2 の 10 乗だから2進数では『10000000000』になる。これをセットするの」
ハル
「なるほど」
エリカ
「最後に仮数部。整数部分の『1』を省略して小数点以下の桁をそのまま入れるの。『1.01』の小数点以下は『01』。つまり『01』を入れて、その後ろは最後まで『0』を埋めればいいの」
ハル
「『1』を省略するのはどうしてですか?」
エリカ
「ビットを節約するため。仮数の先頭は必ず『1』にするから、省略しても問題ないの」
ハル
「ということは、データを読み取るときは先頭に『1』を補う必要があるんですね」
エリカ
「そう。倍精度の仮数部は 52 ビットなんだけど、省略されている 1 ビットを含めて、実際には 53 ビットのデータが入っているのと同じなの。こういうのをケチ表現(economized form)って言うんだって」
ハル
「いいと思います。贅沢は敵です!」

入りきらない数の丸め方

エリカ
「浮動小数点形式のだいたいの仕組みはわかったかな? 同じように『0.1』を倍精度形式にすると、エクセルの演算誤差の正体がはっきり見えるよ」
ハル
「はい、やってみます。まず2進数にするんですよね。『0.1』は 『0.125』までは引けなくて『0.0625』で引くことができます。残りは『0.0375』。次の『0.03125』も引くことができて残りは『0.00625』… えっと… あの… 桁が増えるばかりで残りが 0 になりそうにないんですが…」
0.1を2進数で表現する→2のマイナス10乗でも割り切れない
エリカ
「実は、どこまで計算しても残りは 0 にはならないの。『0.000110011』の後は『0011』が繰り返されるだけ。『0.1』はどんな2の累乗の数でも割り切れない、2進数では表現できない数なんだ」
ハル
「どうして割り切れないんですか?」
エリカ
「理由を説明するのは難しいけど、確かめるのは簡単にできるよ。『0.1』は『10分の1』だよね。『1÷10』を2進数で計算すると、同じ余りが繰り返し現れるから、割り切れないことがわかる。『0.1』は、2の累乗数の中で同じ小数点以下1桁の『0.5』で割り切れなければ、他の2の累乗数でも絶対に割り切れないんだ」
1÷10を2進数で計算すると、絶対に割り切れない
ハル
「2進数に変換できないときはどうすればいいんですか?」
エリカ
「とりあえず適当な桁で打ち切っておけばいいよ。仮数部に入りきらない部分をどうするかは後で説明するね」
ハル
「わかりました。とりあえず『0.1』は2進数では『0.000110011…』ということで計算を続けます。これを浮動小数点形式にすると『0.000110011…×2の0乗』になって、桁をそろえると『1.10011…×2の-4乗』になります。ここまでは、いいですか?」
エリカ
「うん。合ってる」
0.1を倍精度形式にすると桁が入りきらない
ハル
「符号部はプラスだから『0』。指数部は『-4+1023』で『1019』。これは『512+256+128+64+32+16+8+2+1』だから、11 ビットの2進数にすると『01111111011』ですね。それで、仮数部はどうすれはいいんですか?」
エリカ
「仮数部は、まず『1.10011…』の先頭の1を省略して『10011…』を入れるの。そうすると52ビット目までが『…00110011001』で、その続きの『100110011…』は入りきらないよね。この、入りきらない部分は丸めるの」
ハル
「丸めというと、四捨五入するんですか?」
エリカ
「IEEE 754 では丸め方が 4 通りあって、ユーザーが選べるようになっているの。でも普通はデフォルトの最近偶数丸め(Round to Nearest-Even)という方法で丸めるんだ」
ハル
「最近偶数丸め?」
エリカ
「最近偶数丸めは基本的には0捨1入。0 は切り捨てて 1 は切り上げるの。ただし、ぴったり 1 のとき、つまり端数が最後の桁のちょうど半分のときは、丸めた後の数が偶数になるように切り上げたり切り捨てたりするの。偶数丸めと言っても、いつも偶数に丸めるわけではないから注意してね」
ハル
「どうしてそんな複雑なことをするんですか?」
エリカ
「四捨五入や0捨1入では、丸めた後の合計の方が、丸める前の合計よりも大きくなる傾向があるの。例えば、小数点以下 2 桁の2進数の中で、0捨1入して『1』になる数は『0.10』『0.11』『1.00』『1.01』の 4 個ある。この合計は『11.1』つまり 3.5 なんだけど、丸めた後の合計は 4。つまり 0.5 だけ大きくなるの」
「0捨1入」と「最近偶数丸め」の丸め前後の合計を比較
ハル
「本当に大きくなりますね」
エリカ
「最近偶数丸めでは『1』になる2進数は『0.11』『1.00』『1.01』の 3 つだけ。『0.10』は端数が 0.1 ちょうどだから、奇数の『1』ではなく、偶数の『0』に丸めるの。丸める前の合計は『11』つまり 3 で、丸めた後の合計も同じ 3 になる。丸めて『2』になる数も、丸めの前と後の合計が同じになる。つまり全体としては最近偶数丸めの方が、丸めによる変化が小さくなるんだ」
ハル
「なるほど…」
エリカ
「それじゃ、仮数部の計算に戻るよ。仮数部に入りきらない『100110011…』は、最後の桁の半分より大きいから切り上げになるよね。52 ビット目までは『…00110011001』だから、最後の桁に切り上げた分の 1 を足して『…00110011010』にする。これを仮数部にセットすれば、でき上がり!」
仮数部に入りきらない桁を丸める
ハル
「0.1 を倍精度形式にすると、入りきらない部分が切り上げられて、その分だけ本当の 0.1 よりも大きくなるんですね」
エリカ
「そう。これを『丸め誤差』って言うの。10進小数は2進数には正確に変換できないんだ。反対に、2進数から10進数への変換は正確にできる。0.1 の指数は -4 だったよね。つまり、仮数部の先頭に隠れている『1』の大きさは『2の-4乗』。仮数部の最初の桁は『2の-5乗』、2番目の桁は『2の-6乗』、最終桁は『2の-56乗』になる。1になっている桁をすべて足せば10進数に変換できるの。そうすると、前に説明した、0.1 よりちょっとだけ大きい不思議な数になるわけ」
ハル
「そうか… あの長い不思議な数の意味がやっとわかりました!」
エリカ
「誤差の大きさも倍精度形式からわかってくるよ。仮数部の最終桁は、仮数部全体を52桁、右にシフトさせた数とほぼ同じ大きさになる。つまり、仮数部の最終桁は、数に『2の-52乗』を掛けたものとほぼ同じになるの。丸め誤差の大きさは仮数部の最終桁の半分以下。だから、数に『2の-53乗』を掛けたものが誤差の最大値だってことが言えるの」
ハル
「そう言えば、誤差対策の中でそんな話がありましたね。仕組みがわかると、いろいろなことが一つに繋がってきますね!」

ユーザー定義関数の作り方

エリカ
「ところで、ハルちゃんは VBA(ブイ・ビー・エー)は知ってる?」
ハル
「エクセルのマクロのことですよね。Visual Basic for Applications(ビジュアル・ベーシック・フォー・アプリケーションズ)。名前だけなら知っています」
エリカ
「エクセルのワークシートでは数値のデータの種類は一つしかないけど、VBA には何種類もあって、誤差のないデータ型もあるんだ」
ハル
「え? そうなんですか?」
エリカ
「ちょっと試してみようか。VBA を使って消費税を計算するユーザー定義関数を作ってみるね」
ハル
「おもしろそうですね」
エリカ
「まず、[ツール]-[マクロ]-[Visual Basic Editor] で、Visual Basic Editor を起動する」
ハル
「あ、ウィンドウが開きました」
Visual Basic Editorのウインドウ
エリカ
「次に、左上の『プロジェクト』ウィンドウでマクロを作成するブックを選択して、[挿入]-[標準モジュール] を実行するの。コードを書くための専用のシートができるよ」
ハル
「はい。『Module1』というウィンドウが開きました」
標準モジュールの作成
エリカ
「この『Module1』にコードを書くの」
Function 消費税1(金額 As Double, 税率 As Double) As Double 消費税1 = Fix(金額 * 税率) End Function
エリカ
「このコードでは『消費税1』というユーザー定義関数を定義しているの。コードの説明は後にして、実際にこの関数を使ってみるね。エクセルのウィンドウに戻って、A2 に『1000』、B2に『5%』、C2に『=消費税1(A2,B2)』と入れるの」
ハル
「『\50』という答えが表示されました。ユーザー定義関数って、結構、簡単に作れるんですね」
「=消費税1(A2,B2)」と、ユーザー定義関数を入力した
エリカ
「それじゃ、コードの説明をするね。1行目の『Function 消費税1』は関数名の定義。『(金額 As Double, 税率 As Double)』の部分は引数(ひきすう)の定義。この関数を使うときは金額と税率を関数に渡すってこと。『Double』(ダブル)というのは倍精度浮動小数点型という意味で、IEEE 754 の倍精度形式のことなんだ。単精度もあって『Single』(シングル)って言うの。それから、1行目の最後の『As Double』は、このユーザー定義関数が返す値も Double 型だという意味」
ユーザー定義関数の説明図
ハル
「1行目でワークシートから金額と税率を受け取っているわけですね」
エリカ
「そう。2行目の Fix 関数は小数点以下を切り捨てて整数にする関数。『金額 * 税率』の結果を切り捨てているの。『消費税1 = 』の部分は、計算結果をこの関数の戻り値に代入するという意味。関数の中で『消費税1』に代入した値がこの関数の戻り値になって、セルに表示されるというわけ」
ハル
「切り捨てには Fix 関数を使うんですね。四捨五入や切り上げはどんな関数を使うんですか?」
エリカ
「VBA には四捨五入や切り上げをする組み込みの関数はないの。でも、四捨五入は 0.5 を足して切り捨てればいいし、切り上げは、例えば小数点以下 2 桁までの数なら 0.99 を足して切り捨てればいい。誤差のあるデータなら微小値を使えばいい。何とかなるよ」
ハル
「なるほど… 切り捨ての関数だけでもできるんですね」

Double型の誤差

エリカ
「Double 型で誤差が出ることを確認してほしかったので、この関数にはまだ誤差対策は何もしてないんだ。『Fix(金額 * 税率)』の切り捨てで余分に切り捨てられる可能性があるから誤差対策は必要だよね。ただ、とりあえず税率が 5% なら、余分に切り捨てられることはないの」
ハル
「え? どうしてですか?」
エリカ
「5% つまり 0.05 は倍精度形式では本当の 0.05 より大きい数なんだ。だから掛け算した結果も本当の数より大きくなることはあっても小さくなることはないの。余分に切り捨てられてしまう可能性があるのは、3% とか 6% とか、本当の数より小さい数の場合。例えば、金額を『\1,000』、税率を『6%』にすると『\59』になってしまう」
ハル
「あ、本当ですね」
税率を6%にすると余分に切り捨てられてしまう
エリカ
「誤差対策とは言えない方法だけど、Fix 関数の中を『CDbl(金額 * 税率)』とすると答えは『\60』になる。CDbl 関数はデータを Double 型に変換する関数。もう一つ『消費税2』という関数を作ってみるね」
Function 消費税2(金額 As Double, 税率 As Double) As Double 消費税2 = Fix(CDbl(金額 * 税率)) End Function ←計算結果をCDbl関数でDouble型に変換する
税率が6%でも正しい答えになった
ハル
「確かに CDbl関数を使うと誤差が出ませんね… でも、金額も税率も Double 型なんですよね? 『金額 * 税率』の結果は Double 型ではないということですか?」
エリカ
「実はそうなんだ。計算式の中では『金額 * 税率』は一時的に拡張精度になっているの」
ハル
「拡張精度?」
エリカ
「IEEE 754 のデータ形式の中に拡張単精度、拡張倍精度というのがあったでしょう? あれのこと。今、私達が使っているこのパソコンにはインテル社の FPU(浮動小数点演算装置)が入っているんだけど、この FPU は倍精度形式も単精度形式も、仮数部が 64 ビットの拡張精度形式に変換して計算しているの」
ハル
「仮数部が 64 ビットということは 53 ビットの倍精度より大きいんですね」
エリカ
「そう。つまり、『金額 * 税率』の結果は、拡張精度のままでは 60 より小さい数なので切り捨てられてしまうけれど、倍精度にすると切り上げられて 60 になるので、切り捨てられないというわけ。でも、倍精度にしても期待通りに丸められるとは限らないけどね。例えば、消費税率としては大きすぎるけど、金額を『\1,400』、税率『70%』にすると『\980』になるはずが『\979』になってしまう。結局、この方法は誤差対策にはならないの」
Double型にしても数値によっては誤差は出る

通貨型と10進型

ハル
「Double 型ではワークシートと同じように誤差が出るんですね。それで、誤差のないデータ型というのはどんなものなんですか?」
エリカ
「通貨型と10進型というのがあるの。通貨型は Currency(カレンシー)型とも言うの。名前の通り金額計算用で、整数 15桁、小数点以下 4 桁の固定小数点数なんだ。小数点以下の数にも誤差がないの」
ハル
「どうして小数点以下のデータに誤差がないんですか?」
エリカ
「通貨型は内部的には 10000 倍した整数でデータを格納しているの。だから誤差がないわけ」
ハル
「そうなんですか」
エリカ
「通貨型に変換するには CCur 関数を使うの。金額と税率を通貨型にして計算すれば誤差のない計算ができるよ。新しく『消費税3』という関数を作ってみるね」
Function 消費税3(金額 As Double, 税率 As Double) As Double 消費税3 = Fix(CCur(金額) * CCur(税率)) End Function ← CCur関数で通貨型に変換して計算する
通貨型を使えば誤差は出ない
ハル
「整数 15桁、小数点以下 4 桁もあれば、普通の計算には充分ですね。エクセルでもこういうデータが使えたらいいのに…」
エリカ
「もう一つ、小数の誤差のないデータ型に10進型があるの。Decimal(デシマル)型とも言う。 小数も10進数で格納するから誤差がないの。一応、浮動小数点なんだけど、広い範囲の数値は扱えなくて、『10の-28乗』から『10の28乗』の範囲だけ。数の桁数は 28 桁までいいの。Double 型やワークシートでは扱えない 16 桁以上の数でも計算できるんだ。10進型に変換するには CDec 関数を使うの」
CDec関数で10進型に変換して計算する
10進型を使えば誤差は出ない
ハル
「10進型は 28 桁まで計算できるんですか… VBA には便利なデータ型があっていいですね」
エリカ
「そうだね。でも、普段は Double 型や Single 型を使って、必要なときだけ通貨型や10進型を使うという人の方が多いと思う」
ハル
「え? どうして普段から使わないんですか? 誤差の心配がいらないのに…」
エリカ
「通貨型や10進型にも気にしないといけないことがいろいろあるんだ。扱う数値の範囲を気にしないといけないし、通貨型や10進型が使えない関数もある。総合的には Double 型や Single 型の方が制約が少ないんだと思う。それから10進型は他のデータ型に比べて計算に時間がかかるの。できれば使いたくないものなんだ」
ハル
「そうなんですか…」
エリカ
「いずれは10進小数の誤差なんか気にしなくてもいい時代が来るとは思うけど、まだしばらくの間は誤差と付き合わないといけないんじゃないかな」
ハル
「そうですね。でも、誤差の対策も、理由もわかったので、大丈夫だと思います。ありがとうございました!」
エリカ
「そう。頑張ってね!」
ハル
「…あ、先輩!」
エリカ
「どうしたの?」
ハル
「早速、つまづいてしまいました… ユーザー定義関数を作ったこのファイルはどうすればいいんですか?」
エリカ
「普通に名前を付けて保存しておけばいいよ。ファイルを開けばまた同じように使える。もっと VBA について知りたいなら入門書で勉強してね。私の持ってる本を貸してあげようか?」
ハル
「ありがとうございます。ぜひ、お願いします!」

第4回のまとめ

  • IEEE 754 浮動小数点演算規格は現在広く使われている数値計算の標準規格。
  • 浮動小数点数は『(仮数)×(基数)の(指数)乗』という形式で表現された数値のこと。
  • IEEE 754では2進数を使うため、ほとんどの10進小数は正確に表現できない。
  • VBA の通貨型や10進型では、誤差のない10進小数を格納できる。

↑ページの先頭へ←直前のページへ