HOWTO · C

C 言語で数値を科学的に表示する

この記事では、C 言語で数値を科学的記数法で表示する方法を紹介します。

このページの内容

printf の %e または %E 変換を使うと、浮動小数点値を科学的記数法で表示できます。小文字の変換では指数記号も小文字になり、大文字の変換では大文字になります。%.3e のように精度を指定すると、仮数部の小数点以下の桁数を設定できます。

次の例を実行します。

このセクションのコードと表示されるラベルや出力は英語のままです。

#include <stdio.h>

int main(void) {
    double values[] = {123456.789, -0.000012345};

    printf("lowercase: %.3e\n", values[0]);
    printf("uppercase: %.3E\n", values[1]);
    return 0;
}

Cで科学的記数法がどのように機能するか

科学的記数法では、0でない値を仮数部と10のべき乗の積で表します。仮数部は小数点の前に0でない桁が1つだけあり、指数は小数点をどれだけ移動するかを示します。たとえば、123456 は 1.23456 × 10^5、0.0000123 は 1.23 × 10^-5 と表せます。

この記法が変えるのは値の表示方法であり、基になる float や double の保存方法ではありません。2進浮動小数点型の精度には限りがあるため、多くの10進小数はメモリ上で近似値になります。表示桁数を増やすと、保存された近似値のより多くの桁が見えることがありますが、保存値自体の精度が上がるわけではありません。

printfで科学的記数法を表示する

e と E の変換指定子は、double 引数を科学的記数法で書式化します。指数記号の大文字・小文字は変換指定子に従います。%e は e、%E は E を使います。指数には符号が含まれ、実装は指数を表すのに十分な桁数を出力します。比較のために固定小数点表記を使いたい場合は %f を使います。POSIXの fprintf 仕様では、e と E の変換と精度の動作が定義されています。

冒頭の例では、%.3e と %.3E はどちらも仮数部の小数点以下に3桁を表示します。値の符号も書式化された数値に含まれます。固定小数点表記では読み取りにくい範囲の値でも、科学的記数法なら仮数部を短く保ち、指数で大きさを示せます。

この変換指定子が受け取る引数は double です。printf に渡された float は double に昇格しますが、整数は昇格しません。整数値を科学的記数法で書式化するには、たとえば (double)count のように明示的に変換してから %e または %E を使います。double で表現できる精度を超える大きな整数は、変換時に下位の桁が失われることがあります。

小数点以下の桁数を指定する

%e と %E では、精度は有効数字の合計ではなく、小数点以下の桁数を指定します。そのため %.2e は仮数部の小数点以下を2桁表示し、通常は有効数字が合計3桁になります。精度を省略した場合、既定値は小数点以下6桁です。値は指定した精度に合わせて表示上丸められますが、保存値は変わりません。POSIXの fprintf 仕様には、精度の規則と小数点以下6桁という既定値が記載されています。

精度は、アプリケーションで必要な表示形式やデータ交換形式に合わせて選びます。表示桁数を増やしても、10進文字列が2進浮動小数点値を正確に表すとは限りません。特定の有効桁数が必要な場合は、仮数部の先頭の1桁を考慮して精度を決めてください。

科学的記数法の文字列を安全に書式化する

書式化した数値を保存したり、別の関数に渡したりする場合は、バッファ容量を指定して snprintf を使います。容量には終端のヌル文字も含めます。戻り値は、終端文字を除いて書き込まれるはずだった文字数です。戻り値が容量以上なら出力は切り詰められています。負の戻り値は書式化エラーを示します。POSIXの fprintf 仕様では、snprintf の戻り値と切り詰めの動作が定義されています。

次の自己完結した例を実行します。

このセクションのコードと表示されるラベルや出力は英語のままです。

#include <stdio.h>

int main(void) {
    double measurement = 0.000012345;
    char text[64];
    int length = snprintf(text, sizeof text, "%.3e", measurement);

    if (length < 0) {
        puts("formatting error");
        return 1;
    }
    if ((size_t)length >= sizeof text) {
        puts("buffer too small");
        return 1;
    }

    printf("value: %s\n", text);
    return 0;
}

snprintf は利用可能な容量を超えて書き込まず、容量が0より大きければバッファを終端します。バッファ全体に書式化結果が入ったものとして扱う前に、戻り値を確認してください。バッファに収まる保証がない場合は、境界のない sprintf を避けます。

独自の科学的記数法関数を書くべきか

通常は不要です。%e と %E は、Cライブラリの書式化規則に従って、仮数部、指数、符号、丸め、そして値が0の場合をすでに処理します。独自に変換処理を書くなら、0を個別に扱い、1未満の値と10以上の値の両方を正規化する必要があります。また、負の値、丸めによって仮数部が変わるケース、数値型の範囲も考慮しなければなりません。正の値を10で割るだけのループでは、これらのケースを正しく扱えません。

画面へ直接表示する場合は printf、上限のあるバッファに文字列を作る場合は snprintf を使いましょう。標準の変換指定子では表せない特定の形式が必要な場合に限り、独自のフォーマッターを作り、入力範囲全体での動作を定義して確認してください。一般的な科学的記数法なら、標準の変換指定子のほうが短く、誤りも起きにくくなります。