はじめMath! Javaでコンピュータ数学

第10回 引き算はコンピュータの弱点[前編]

この記事を読むのに必要な時間:およそ 2 分

桁落ち

桁落ちとは,値が大変近い浮動小数点数の減算の結果,有効数字※2の桁数が減少してしまうことです。

IEEE754単精度では仮数部が10進数にして約8桁,倍精度でも約15桁の桁数しか表現できません。桁落ちにより簡単にとんでもない計算結果になる可能性があります。近接する値の減算を繰り返し行う場合には,十分検討が必要です。桁落ちを避けるためには,式変形により減算を加算に変形する方法があります。

値が大変近い数値の減算例として,次の式を見てみましょう。

9の二乗の81にわずかに満たない数値の平方根は,9に大変近い数値になるでしょう。それぞれの数値を今回はfloat型に格納して計算してみましょう※3⁠。float型は10進数にして仮数部が8桁程度です。最も下位の桁には丸めが入っていますから数値として正確であることが期待できるのは7桁程度です。つまり,今回の計算は有効数字7桁の計算であるといえます。計算をする人は計算結果に有効数字7桁が保証されることを期待します。

さて,この計算をJava言語で次のように計算させてみましょう。

  float a = 9;
  float b = (float) Math.sqrt(80.9);
  float c = a - b;

なんの変哲もありません。ただ引き算をしただけです。しかしこの計算の結果は0.0055570602になります。試しにWindowsの電卓を用いて計算してみると0.0055572712924565534295443750356158という値が得られます。こちらが正しいとすると,一致する桁はたったの4桁しかありません。桁落ちが発生したのです。これは困ったことです。たった一度の減算を行っただけで,精度がこんなに落ちてしまいました。

この現象は次のように説明できます。float型に数値を格納すると,数値は仮数部と指数部に分けられます。仮数部には表現可能な桁数に限界があり,それが10進数にしておよそ8桁です。最大で8桁の有効数字であるといえます。8桁の有効数字を持つfloat型で,今回の計算は次のように行われます。浮動小数点数の加減算は指数部を同じ値にそろえ,仮数部のみの演算を行います。

浮動小数点数は,この計算結果を有効数字8桁分で保持します。更に,計算結果の有効数字が減った分をゼロで補い,その値の近似値を保持しますから,計算結果の有効数字4桁目か5桁目からは真の値からずれていきます。

浮動小数点数の減算は,このような問題点を持っています。桁落ちを回避するためには,近接する値の減算が予想される場合に減算を用いない計算方法をしなければなりません。

例えば次のように式を変形することで回避できます。

このように式を変形してJava言語で計算すると0.0055572717という結果が得られました。先ほどの電卓の計算結果と比較すると,7桁一致しました。4桁だった精度が7桁まで回復しています。

同様の計算でよく問題になるのは,二次方程式の解の公式です。

bの値が正で,4acの値が非常に小さい場合,次の式の分子の計算で桁落ちの発生する危険性があります。

この式での桁落ちを回避するためにどのように式変形できるでしょうか。これを今回の演習としましょう。

※2
ある数値が実際の目的に有効で意味のある桁数を持っている場合,これを有効数字といいます。有効桁数ということもあります。
※3
float型は科学技術計算には精度が不十分ですから,実際にはdouble型を用いましょう。今回は学習の利便のため,わざとfloat型を選択しています。

今回はここまで

数式が多くなりましたが避けて通れない関門です。しっかり手と頭を使ってクリアしましょう。

今回のまとめ

  • オーバーフロー/アンダーフローを避ける為に,計算途中の値にも注意しましょう。
  • 桁落ちには式変形で対処しましょう。

著者プロフィール

平田敦(ひらたあつし)

地方都市の公立工業高等学校教諭。趣味はプログラミングと日本の端っこ踏破旅行。2010年のLotYはRuby。結城浩氏のような仕事をしたいと妄想する30代後半♂。