【C言語】fenv.hで浮動小数点の調整【標準ライブラリ】

【C言語】fenv.hで浮動小数点の調整【標準ライブラリ】

C言語のfenv.hを使って出来ることと典型的な使い方

fenv.hとは

fenv.h はC言語の標準ライブラリの一つで、浮動小数点演算に関する制御と例外処理を行うためのヘッダーファイルです。 このライブラリを使うことで、浮動小数点例外(ゼロ除算、オーバーフローなど)の検出や、丸めモードの変更が可能になります。

浮動小数点例外の検出と処理

C言語では、浮動小数点演算中にエラー(例外)が発生しても、通常はプログラムが自動的に停止しません。 しかし、fenv.h を使うことで、特定の例外を検出し、適切に処理することができます。

使用可能な浮動小数点例外

  • FE_DIVBYZERO: ゼロによる除算
  • FE_INEXACT: 精度の損失(丸め誤差)
  • FE_INVALID: 無効な演算(例えば 0/0 や sqrt(-1))
  • FE_OVERFLOW: オーバーフロー
  • FE_UNDERFLOW: アンダーフロー

例: 例外を検出する


#include <stdio.h>
#include <fenv.h>

#pragma STDC FENV_ACCESS ON  // fenv.h の機能を有効化

int main() {
    feclearexcept(FE_ALL_EXCEPT);  // 例外フラグをクリア

    double x = 1.0 / 0.0;  // ゼロ除算を実行

    if (fetestexcept(FE_DIVBYZERO)) {
        printf("ゼロによる除算が発生しました\n");
    }

    return 0;
}

丸めモードの設定と変更

浮動小数点数の計算結果は、通常最も近い値に丸められますが、fenv.h を使うと丸めモードを変更できます。

使用可能な丸めモード

  • FE_TONEAREST: 最近接丸め(デフォルト)
  • FE_DOWNWARD: 負の無限大方向に丸め
  • FE_UPWARD: 正の無限大方向に丸め
  • FE_TOWARDZERO: ゼロ方向に丸め

例: 丸めモードを変更する


#include <stdio.h>
#include <fenv.h>

#pragma STDC FENV_ACCESS ON

int main() {
    fesetround(FE_DOWNWARD);  // 負の無限大方向に丸め
    printf("現在の丸めモード: FE_DOWNWARD\n");

    return 0;
}

浮動小数点例外フラグの操作

浮動小数点演算の結果として発生する例外フラグを明示的に設定・クリアできます。

例: 例外フラグの設定とクリア


#include <stdio.h>
#include <fenv.h>

int main() {
    feclearexcept(FE_ALL_EXCEPT);  // すべての例外フラグをクリア
    feraiseexcept(FE_INVALID);  // 無効な演算例外を発生させる

    if (fetestexcept(FE_INVALID)) {
        printf("無効な演算例外が発生しました\n");
    }

    return 0;
}

浮動小数点環境の保存と復元

一時的に浮動小数点の環境を変更し、後で元に戻すことも可能です。

例: 環境の保存と復元


#include <stdio.h>
#include <fenv.h>

int main() {
    fenv_t env;
    fegetenv(&env);  // 現在の環境を保存

    fesetround(FE_UPWARD);
    printf("丸めモードを FE_UPWARD に変更\n");

    fesetenv(&env);  // 環境を復元
    printf("元の環境に戻しました\n");

    return 0;
}

実用的な使用例

例1: 浮動小数点演算のエラーをログに記録


#include <stdio.h>
#include <fenv.h>

void check_exceptions() {
    if (fetestexcept(FE_DIVBYZERO)) printf("ゼロ除算エラー\n");
    if (fetestexcept(FE_OVERFLOW)) printf("オーバーフロー発生\n");
    if (fetestexcept(FE_UNDERFLOW)) printf("アンダーフロー発生\n");
    feclearexcept(FE_ALL_EXCEPT);  // フラグをクリア
}

int main() {
    feclearexcept(FE_ALL_EXCEPT);

    double x = 1.0 / 0.0;  // ゼロ除算
    check_exceptions();

    return 0;
}

例2: 精度の損失を監視


#include <stdio.h>
#include <fenv.h>

int main() {
    feclearexcept(FE_ALL_EXCEPT);
    double x = 1.0 / 3.0 + 1e-20;  // 精度の損失がある計算

    if (fetestexcept(FE_INEXACT)) {
        printf("精度の損失が発生しました\n");
    }

    return 0;
}
コメントは受け付けていません。