興味の源泉

自分が興味を起こせるようなネタを雑多に書き綴るブログ

変数の精度には気をつけろ!【プログラミング】

注意※この話はC#を元にしますが、どの言語でも同じ概念があります。

有効数字の桁数

変数には扱える数が決まっています。

int なら -2,147,483,648 ~ 2,147,483,647
float なら ±1.5 x 10−45 ~ ±3.4 x 1038

扱える数値の桁数を有効数字の桁数と呼びます。
※以降、長ったらしいので有効桁数と呼ぶ

int の有効桁数は9桁(10桁目は9まで表すことができないので省く)
float は6 ~9 桁(.NetのC#リファレンス参照)

キャスト

有効桁数の話を進めたいところですが、その前に話をしなければならないのがキャストです。

キャストとはある型を別の型に変換することです。

まず下記を見てください。

    int a = 1;
    int b = 3;
    float result = a / b;

resultを出力した結果の値は0です。

なぜか。

int型 を int型 で割り、結果も int 型だからです。
結果をfloatに入れる前にint型として小数点以下は切り捨てられているので0になります。

ではどうすればいいかというと、

    int a = 1;
    int b = 3;
    float result = (float)a / b;

上記のように 変数a または 変数b を floatにキャストすると、計算が floatで行われ、結果が floatになります。

キャストと有効桁数

次に見てもらいたいのがこちら

    int a = 987654321;
    float b = a;
    int result = (int)b;

このresultの出力結果は私の環境では987654336になりました。

ここで有効桁数の話に戻りますが、float の有効桁数は約7桁です。
※なぜ約7桁なのか知りたい場合は浮動小数点数で検索してみてください

int から float に入れられるときに暗黙的に変換が起こり、桁数が足りなくなります。

なので、再度キャストをして int に戻したときに値が不定の桁があり、同じにならなかったということです。

これを解消するためには float ではなくdoubleにキャストすれば同じ値になります。


では次の場合はどうでしょうか。

    int a = 1234567;
    float b = 300.3f;
    float result = a * b;

この計算結果、本当ならば「370740470.1」なのですが、resultは「3.707404E+08」と出力され、intにキャストした場合「370740455」と出力されました。

この場合も計算結果が有効桁数より足りていないのが原因です。

float と double の変換の注意点

ではfloatをdoubleにキャストすれば良いじゃん、ということでやってみると

    int a = 1234567;
    float b = 300.3f;
    double c = (double)b;
    double result = a * c;

cの値は「300.299987792969」となり、resultの出力は「370740455.029602」となり、正しい値にはなりませんでした。

こういう挙動になる原因はおそらく、浮動小数点数は値を近似値として扱っているのが原因なのではと思います。
ここでの解決方法は、floatをdoubleにキャスト後、指定の桁数で丸めることです。

    int a = 1234567;
    float b = 300.3f;
    double c = (double)System.Math.Round((double)b, 1);
    double result = a * c;

これで cの値は「300.3」になり、resultの出力は「370740470.1」となって正しい値になりました。


まとめ

今回まとめてみて、floatとdoubleの変換でずれるのはやばいなと。

数字を扱う以上は小数点以下の扱いをどうするかは付きまといます。

どの型を使うのか。

小数点第何位までを有効として扱うか。

切り上げるのか、切り捨てなのか、四捨五入なのか。

意識して処理しないといけないなと今更ながらかみしめるのでした。