トラック5・📦変数・型・演算子

浮動小数点数 float / double

読む目安 約12分・ゴール:doubleを既定として使い分け、小数に必ず誤差があることと比較の作法を理解する

アプリでこのトピックを学ぶ(無料・登録不要)

このトピックの要点

平均点、身長、円周率。世の中の数値の多くは整数では表せません。小数を扱う型がdoubleです。

まずは double を使う

#include <stdio.h>

int main(void) {
    double shinchou = 1.725;   // メートル
    double taijuu   = 61.25;   // キログラム
    double bmi = taijuu / (shinchou * shinchou);

    printf("BMIは %.1f です\n", bmi);
    return 0;
}

コードに小数を書くと、その値は既定で double として扱われます。表示は %f(既定で小数点以下6桁)、%.1f のように桁数を指定できます。

float と double の違い

・float … 4バイト。有効桁はおよそ7桁。1.5f のように末尾に f を付ける。
・double … 8バイト。有効桁はおよそ15〜16桁。迷ったらこちら。

double は「倍精度」という意味です。メモリが厳しい組込みや、膨大な数値を並べる場面を除けば、double が標準の選択になります。

動く例——誤差を目で見る

#include <stdio.h>

int main(void) {
    double a = 0.1 + 0.2;

    printf("%.1f\n", a);
    printf("%.17f\n", a);
    printf("%d\n", a == 0.3);
    return 0;
}

出力は 0.3、0.30000000000000004、0 です。0.1 も 0.2 も、2進数では割り切れない循環小数になります。10進数で 1/3 が 0.333… と書き切れないのと同じ理由で、ごく小さな誤差が必ず残ります。バグではなく、仕組み上そうなります。

だから小数は == で比べない

#include <stdio.h>
#include <math.h>

int main(void) {
    double a = 0.1 + 0.2;

    if (fabs(a - 0.3) < 1e-9) {
        printf("ほぼ等しい\n");
    }
    return 0;
}

「差の絶対値が十分小さければ等しいとみなす」——これが小数の比較の定石です。fabs は math.h の絶対値関数、1e-9 は 0.000000001 という書き方です。

罠・注意

printf は %f、scanf は %lf。 double を読むときだけ l が付きます。非対称なので間違えやすいところです。

お金の計算に double は向かない。 誤差が積み上がると1円ずれます。金額は最小単位(銭・円)の整数で持つのが定石です。

表示は丸められている。 %.1f で 0.3 と見えても、中身は 0.30000000000000004 のまま。見た目と中身を混同しないことです。

師範小数の等号は、砂粒の数を目で比べるようなものじゃ。「同じか」ではなく「近いか」を問え。

まとめ

小数は double を既定に。float は精度がおよそ半分で、末尾に f が要ります。2進数で表せない値には必ず誤差が乗るため、== ではなく差の大きさで比較します。printf は %f、scanf は %lf。

アプリでこのトピックを学ぶ(無料・登録不要)

確認問題

確認問題 1

小数を扱うとき、まず選ぶべき型はどれですか。

  1. float(4バイト・有効桁およそ7桁)
  2. double(8バイト・有効桁およそ15〜16桁)
  3. int(4バイト・整数のみ)
  4. char(1バイト)
答えを見る

正解:B(double(8バイト・有効桁およそ15〜16桁))

Cではコードに書いた小数が既定でdoubleとして扱われ、精度も十分にあります。float は半分のサイズで有効桁も約7桁と少なく、末尾に 1.5f のように f を付ける必要があります。メモリが厳しい場面以外は double が標準の選択です。

確認問題 2

このプログラムの出力はどれですか(/は改行を表します)。

#include <stdio.h>

int main(void) {
    double a = 0.1 + 0.2;
    printf("%.1f\n", a);
    printf("%d\n", a == 0.3);
    return 0;
}
  1. 0.3/1
  2. 0.3/0
  3. 0.30000000000000004/1
  4. 0.3/コンパイルエラー
答えを見る

正解:B(0.3/0)

0.1 も 0.2 も2進数では割り切れないため、足した結果は 0.30000000000000004 のようなわずかにずれた値になります。%.1f は小数第1位に丸めて表示するので 0.3 に見えますが、中身は 0.3 と一致しないので a == 0.3 は偽=0 です。小数の比較は fabs(a - 0.3) < 1e-9 のように差の大きさで行います。

「変数・型・演算子」の目次

  1. 変数とは名前付きの箱
  2. int型と整数の世界
  3. 浮動小数点数 float / double
  4. char型と文字コード
  5. bool型と真偽——C23標準の書き方
  6. 定数——const と constexpr
  7. 算術演算子と優先順位
  8. 整数の割り算と剰余の罠
  9. 型変換——暗黙と明示
  10. 2進数・16進数リテラル(0b / 0x / 桁区切り')
  11. sizeofとメモリ上のサイズ
  12. オーバーフローという事件

もっと先へ:ポインタ・メモリ・ファイル入出力・セキュアコーディングを含む全26トラックと、段位検定・模試のフルセットは完全版に収録しています。

完全版の販売ページは準備中です。

ほかのトラック

📚 姉妹教材:手を動かして覚える Linux 教材 — Linuxとインフラの仕組みを地図で学ぶ

Web版