C++のdoubleについて

AI実装検定のご案内

C++のdoubleは、小数を含む数値を扱うための浮動小数点型です。

3.140.001-12.5のような値を表現したい場合に使用します。

intが整数を扱う型であるのに対して、doubleは小数を含む値を近似的に表現できます。

基本的な書き方は次のとおりです。

double pi = 3.1415926535;
double price = 1980.5;
double temperature = -2.7;

doubleは、C++で数値計算を行う際に非常によく使われる基本データ型の1つです。

目次

C++でdoubleを使う基本的な方法

double型の変数を宣言する

double型の変数は、次の形式で宣言します。

double 変数名;

例えば、次のように記述できます。

double height;
double weight;

宣言と同時に値を設定することもできます。

double height = 175.5;
double weight = 68.2;

C++11以降では、波かっこを利用した初期化も可能です。

double height{175.5};
double weight{68.2};

doubleを使って四則演算する

doubleは、整数型と同じように四則演算に利用できます。

#include <iostream>

int main() {
    double a = 10.5;
    double b = 2.0;

    std::cout << a + b << '\n';
    std::cout << a - b << '\n';
    std::cout << a * b << '\n';
    std::cout << a / b << '\n';
}

実行結果は次のようになります。

12.5
8.5
21
5.25

小数を含む計算を行いたい場合には、doubleが便利です。

doubleとintの違い

intは整数、doubleは小数を扱える

intdoubleの大きな違いは、小数部分を扱えるかどうかです。

int a = 10;
double b = 10.5;

intは整数を扱う型です。
一方、doubleは浮動小数点型なので、小数を含む値を表現できます。

整数同士の除算では小数部分が失われる

次のコードを見てみましょう。

#include <iostream>

int main() {
    int a = 5;
    int b = 2;

    std::cout << a / b << '\n';
}

実行結果は次のとおりです。

2

数学的には5 ÷ 2 = 2.5ですが、abがどちらも整数型なので、整数除算が行われます。

そのため、小数部分は失われます。

一方で、doubleを使うと次のようになります。

#include <iostream>

int main() {
    double a = 5.0;
    double b = 2.0;

    std::cout << a / b << '\n';
}

実行結果は次のとおりです。

2.5

doubleとfloatの違い

doubleはfloatより高い精度を持つ

C++には、double以外にもfloatという浮動小数点型があります。

float a = 3.14f;
double b = 3.14;

一般的な環境では、floatは32ビット、doubleは64ビットで実装されることが多くあります。

ただし、C++規格ではdoubleが必ず64ビット、8バイトであるとは規定されていません。

一般的なIEEE 754環境では、代表的に次のような違いがあります。

一般的なサイズ十進精度の目安
float4バイト約6桁
double8バイト約15桁
long double環境依存double以上

通常の数値計算では、floatより高い精度が必要な場合にdoubleがよく使用されます。

digits10とmax_digits10の違い

浮動小数点型の精度を説明する際には、digits10max_digits10という2つの値があります。

一般的なIEEE 754 binary64のdoubleでは、

digits10     = 15
max_digits10 = 17

となります。

digits10は、十進数からdoubleへ変換し、再び十進数へ戻した際に値を保持できる有効桁数の目安です。

一方、max_digits10は、doubleの値を文字列へ変換し、再びdoubleへ戻したときに元の値を確実に復元するために必要な桁数です。

そのため、単純に「doubleは15~17桁の精度」と考えるより、この2つを区別して理解するほうが正確です。

doubleリテラルの書き方

接尾辞なしの小数はdoubleになる

C++では、接尾辞を付けていない浮動小数点リテラルは基本的にdouble型として扱われます。

double a = 3.14;
double b = 1.5;

この場合、3.141.5double型のリテラルです。

floatにはfを付ける

float型のリテラルとして扱いたい場合は、末尾にfまたはFを付けます。

float a = 3.14f;

long doubleにはLを付ける

long doubleとして扱いたい場合は、末尾にLまたはlを付けます。

long double a = 3.14L;

一般的には、大文字のLのほうが数字の1と見間違えにくいため読みやすいでしょう。

doubleのサイズを確認する方法

sizeofを使う

sizeof演算子を利用すると、使用している環境でのdoubleのサイズを確認できます。

#include <iostream>

int main() {
    std::cout << sizeof(double) << '\n';
}

一般的な環境では、

8

と表示されることが多くあります。

これは8バイトであることを意味します。

ただし、C++規格ではdoubleが必ず8バイトであるとは保証されていません。

doubleの精度を確認する方法

std::numeric_limitsを使う

C++では、<limits>ヘッダーにあるstd::numeric_limitsを使って、doubleの性質を調べることができます。

#include <iostream>
#include <limits>

int main() {
    std::cout << std::numeric_limits<double>::digits10 << '\n';
    std::cout << std::numeric_limits<double>::max_digits10 << '\n';
}

一般的なIEEE 754 binary64環境では、digits10は15、max_digits10は17になります。

処理系に依存する数値特性を確認したい場合には、std::numeric_limitsを利用すると便利です。

doubleでは多くの十進小数を正確に表現できない

0.1は正確に表現できない場合がある

doubleを扱ううえで重要なのが、十進数で記述した小数の多くを完全には表現できないことです。

例えば、

double a = 0.1;

と記述しても、一般的な2進浮動小数点形式では、数学的に完全な0.1ではなく、それに非常に近い値が保存されます。

次のコードで確認できます。

#include <iostream>
#include <iomanip>

int main() {
    double a = 0.1;

    std::cout << std::setprecision(17) << a << '\n';
}

一般的な環境では、次のように表示されることがあります。

0.10000000000000001

これはC++の不具合ではなく、浮動小数点数の仕組みによるものです。

正確に表現できる小数もある

すべての小数が不正確になるわけではありません。

例えば、一般的な2進浮動小数点形式では、

double a = 0.5;
double b = 0.25;
double c = 0.125;

のような値は正確に表現できます。

0.50.25は2進数で有限桁として表現できるためです。

なぜ0.1を正確に表現できないのか

コンピューターでは主に2進数で表現する

一般的なコンピューターでは、浮動小数点数を2進数で表現します。

ところが、十進数の0.1は2進数では有限桁で表現できません。

これは、十進数で1 / 3を表すと、

0.333333333...

と無限に続くのと似ています。

そのため、doubleでは表現可能な最も近い値へ丸めて保存されます。

この仕組みによって、浮動小数点計算ではわずかな誤差が発生することがあります。

doubleを==で比較するときの注意点

計算結果が完全一致しない場合がある

浮動小数点数には丸め誤差が発生する可能性があるため、計算結果を単純に==で比較すると、意図した結果にならない場合があります。

例えば次のコードです。

#include <iostream>

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

    if (a == b) {
        std::cout << "同じ\n";
    } else {
        std::cout << "異なる\n";
    }
}

一般的なIEEE 754 binary64環境では、a == bfalseになることがあります。

許容誤差を使って比較する

単純な用途では、2つの値の差が一定範囲内に収まっているか確認する方法があります。

#include <cmath>
#include <iostream>

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

    double epsilon = 1e-10;

    if (std::abs(a - b) < epsilon) {
        std::cout << "ほぼ同じ\n";
    }
}

ただし、常に1e-10を使えばよいわけではありません。

適切な許容誤差は、値の大きさや計算内容によって異なります。

大きな値や非常に小さな値を比較する場合には、絶対誤差だけでなく相対誤差を考慮することもあります。

doubleで指数表記を使う方法

eを使って指数を表現する

doubleでは、科学技術計算などでよく使われる指数表記を利用できます。

double a = 1.0e3;
double b = 2.5e-4;

それぞれ、

1.0 × 10³ = 1000
2.5 × 10⁻⁴ = 0.00025

という意味です。

非常に大きな値や小さな値を書く場合に便利です。

double distance = 1.496e11;

intをdoubleへ変換する方法

暗黙的に変換できる

整数型の値は、doubleへ変換できます。

int a = 10;
double b = a;

この場合、bには10.0相当の値が格納されます。

static_castを使って明示的に変換する

型変換を明確に示したい場合は、static_castを利用できます。

int a = 5;
int b = 2;

double result = static_cast<double>(a) / b;

結果は、

2.5

になります。

整数除算を避けたい場合に有効な書き方です。

大きな整数では精度が失われる場合がある

整数からdoubleへの変換が常に完全に正確とは限りません。

例えば、非常に大きな整数では、doubleの精度ではすべての整数値を区別できない場合があります。

したがって、大きな整数をdoubleへ変換するときは、精度が失われる可能性がある点に注意が必要です。

doubleをintへ変換する方法

小数部分は0方向へ切り捨てられる

doubleを整数型へ変換すると、小数部分は0方向へ切り捨てられます。

double a = 3.9;
int b = static_cast<int>(a);

bは次の値になります。

3

負の値の場合も同様です。

double a = -3.9;
int b = static_cast<int>(a);

この場合は、

-3

になります。

四捨五入ではない点に注意しましょう。

intで表現できる範囲を超える変換に注意する

さらに重要なのが、変換後の値が整数型で表現可能な範囲を超える場合です。

例えば、

double value = 1e100;
int n = static_cast<int>(value);

のような変換は安全ではありません。

浮動小数点型から整数型への変換では、切り捨て後の値が変換先の整数型で表現できない場合、未定義動作になります。

そのため、大きな値を整数へ変換するときは、事前に範囲を確認する必要があります。

doubleの表示桁数を指定する方法

setprecisionを使う

<iomanip>ヘッダーのstd::setprecisionを使うと、表示精度を調整できます。

#include <iomanip>
#include <iostream>

int main() {
    double pi = 3.141592653589793;

    std::cout << std::setprecision(5) << pi << '\n';
}

std::fixedを指定していない場合、setprecisionは基本的に有効桁数を指定します。

fixedとsetprecisionを組み合わせる

小数点以下の桁数を指定したい場合は、std::fixedと組み合わせます。

#include <iomanip>
#include <iostream>

int main() {
    double price = 1234.56789;

    std::cout << std::fixed
              << std::setprecision(2)
              << price << '\n';
}

実行結果は次のようになります。

1234.57

この場合、std::setprecision(2)は小数点以下2桁という意味になります。

doubleで扱える特殊な値

無限大を表現できる環境がある

IEEE 754に対応した一般的な環境では、doubleで無限大を表現できます。

#include <limits>

double inf = std::numeric_limits<double>::infinity();

ただし、すべての処理系で無限大が利用できるとは限りません。

対応状況は、

std::numeric_limits<double>::has_infinity

で確認できます。

NaNを表現できる環境がある

NaNは「Not a Number」の略で、数値として表現できない結果を示す特殊な値です。

#include <limits>

double nan = std::numeric_limits<double>::quiet_NaN();

NaNかどうかを判定するときは、std::isnanを使用できます。

#include <cmath>

if (std::isnan(nan)) {
    // NaNの場合の処理
}

NaNに対応しているかどうかは、

std::numeric_limits<double>::has_quiet_NaN

などで確認できます。

doubleの最大値と最小値を調べる方法

numeric_limitsで確認する

std::numeric_limitsを利用すると、doubleで扱える数値範囲を確認できます。

#include <iostream>
#include <limits>

int main() {
    std::cout << std::numeric_limits<double>::max() << '\n';
    std::cout << std::numeric_limits<double>::lowest() << '\n';
    std::cout << std::numeric_limits<double>::min() << '\n';
}

minとlowestの違いに注意する

std::numeric_limits<double>::min()は、最も小さい負の値を意味するわけではありません。

浮動小数点型の場合、通常は0より大きい正規化数のうち最小の値を表します。

一方、

std::numeric_limits<double>::lowest()

は、その型で表現できる最も小さい有限値、つまり最も負側の値を取得します。

min()lowest()は意味が大きく異なるため注意が必要です。

金額計算でdoubleを使う場合の注意点

金額をdoubleで扱うと誤差が生じる可能性がある

doubleは多くの十進小数を正確には表現できません。

そのため、金額を厳密に扱う処理では注意が必要です。

例えば、

double price = 0.1;
double tax = 0.2;

のような値にも、内部的には微小な誤差が含まれる可能性があります。

最小通貨単位を整数で管理する方法がある

金額を厳密に管理する方法として、最小通貨単位を整数で保存する方法があります。

日本円であれば、

long long price = 1980;

のように円単位で管理できます。

ドルなど小数単位が存在する通貨では、

long long priceInCents = 1999;

のように、19.99ドルを1,999セントとして扱う方法があります。

ただし、実際の金融システムでは、税計算や丸め方法、通貨ごとの小数桁、オーバーフローなども考慮する必要があります。

doubleがよく使われる場面

数値計算や計測値に適している

doubleは、次のような用途でよく利用されます。

  • 平均値の計算
  • 割合や比率の計算
  • 距離や速度の計算
  • 座標計算
  • 統計処理
  • 科学技術計算
  • シミュレーション
  • グラフィックス
  • 温度や重量などの計測値

例えば、平均値を計算する場合は次のように記述できます。

#include <iostream>

int main() {
    int total = 250;
    int count = 3;

    double average =
        static_cast<double>(total) / count;

    std::cout << average << '\n';
}

整数除算を避けるために、static_cast<double>で明示的に変換しています。

C++でdoubleを使うときの注意点

小数を常に正確に保存できるわけではない

doubleでは、0.10.2など、多くの十進小数を厳密には表現できません。

計算結果には微小な丸め誤差が含まれる可能性があります。

計算結果を安易に==で比較しない

浮動小数点計算の結果を比較する場合、誤差の影響によって完全一致しないことがあります。

必要に応じて、用途に応じた許容誤差を設定して比較しましょう。

整数除算に注意する

次のコードでは、

double result = 5 / 2;

5 / 2が先に整数として計算されるため、resultには2.0相当の値が格納されます。

小数として計算したい場合は、

double result = 5.0 / 2.0;

または、

double result =
    static_cast<double>(5) / 2;

と記述します。

型変換による精度低下や範囲外に注意する

整数からdoubleへの変換では、大きな整数の精度が失われる場合があります。

反対に、doubleから整数型への変換では、小数部分が0方向へ切り捨てられます。

さらに、変換後の値が整数型の表現可能範囲を超える場合には安全な変換にならないため、値の範囲にも注意が必要です。

C++のdoubleについてのまとめ

C++のdoubleは、小数を含む数値を扱うための代表的な浮動小数点型です。

一般的な環境ではIEEE 754の倍精度浮動小数点数として実装され、8バイトで約15桁の十進精度を持つことが多くあります。

基本的な使い方は非常にシンプルです。

double value = 3.14;

ただし、doubleは多くの十進小数を完全には表現できないため、浮動小数点特有の丸め誤差が発生する可能性があります。

特に、double同士の比較、整数除算、整数型との型変換、金額のように厳密性が求められる計算では注意が必要です。

doubleの仕組みや精度、型変換の特徴を理解して適切に利用することが、C++で安全かつ正確な数値計算を行うために重要です。

以上、C++のdoubleについてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次