C言語でアンダーフローを防ぐ!「FE_UNDERFLOW」の処理方法とサンプルコード集


アンダーフローが発生すると、計算結果が 0 または最小正数値に変換される可能性があります。これは、計算精度を失うだけでなく、予期しない結果につながる可能性があるため、注意が必要です。

FE_UNDERFLOW を処理するには、以下の方法があります。

  1. fetestexcpt 関数を使用して、アンダーフローフラグが設定されているかどうかを確認します。
  2. feclearexcept 関数を使用して、アンダーフローフラグをクリアします。
  3. fegetexcept 関数を使用して、現在の例外マスクを取得します。
  4. fesetenv 関数を使用して、現在の例外マスクを設定します。

以下のコード例は、fetestexcept 関数を使用して FE_UNDERFLOW フラグを確認する方法を示しています。

#include <fenv.h>

int main() {
  float x = 1.0f / 1000000000.0f;

  if (fetestexcept(FE_UNDERFLOW)) {
    printf("アンダーフローが発生しました\n");
  } else {
    printf("アンダーフローは発生しませんでした\n");
  }

  return 0;
}

FE_UNDERFLOW を処理するもう 1 つの方法は、feclearexcept 関数を使用してアンダーフローフラグをクリアすることです。これにより、以降の浮動小数点演算でアンダーフローが発生しても、FE_UNDERFLOW フラグが設定されなくなります。

以下のコード例は、feclearexcept 関数を使用して FE_UNDERFLOW フラグをクリアする方法を示しています。

#include <fenv.h>

int main() {
  float x = 1.0f / 1000000000.0f;

  feclearexcept(FE_UNDERFLOW);

  if (fetestexcept(FE_UNDERFLOW)) {
    printf("アンダーフローが発生しました\n");
  } else {
    printf("アンダーフローは発生しませんでした\n");
  }

  return 0;
}

FE_UNDERFLOW は、浮動小数点演算で一般的な問題です。fetestexceptfeclearexcept などの関数を使用して FE_UNDERFLOW を処理することで、プログラムの精度と信頼性を向上させることができます。

  • 詳細については、C 言語リファレンスまたは C++ 言語リファレンスを参照してください。
  • FE_UNDERFLOW は、C++ の fenv ヘッダーファイルでも定義されています。


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

int main() {
  float x = 1.0f / 1000000000.0f;  // 非常に小さな値を計算

  printf("x = %e\n", x);  // 結果を表示

  if (fetestexcept(FE_UNDERFLOW)) {
    printf("アンダーフローが発生しました\n");
  } else {
    printf("アンダーフローは発生しませんでした\n");
  }

  return 0;
}

このコードを実行すると、以下の出力が得られます。

x = 1.000000e-08
アンダーフローが発生しました

出力結果からもわかるように、x 変数は非常に小さな値 (10^-8) になっています。このような小さな値を計算すると、FE_UNDERFLOW が発生する可能性があります。

上記のコードでは、fetestexcept 関数を使用して FE_UNDERFLOW フラグを確認しています。このフラグが設定されている場合、アンダーフローが発生しました と表示されます。

FE_UNDERFLOW を処理するには、さまざまな方法があります。以下に、いくつかの例を示します。

  • 例外ハンドラを使用する
    FE_UNDERFLOW が発生した場合に実行されるコードを記述できます。
  • 計算結果を丸め処理する
    計算結果を丸め処理することで、0 または最小正数値に変換されるのを防ぐことができます。
  • 計算精度を変更する
    より高い精度で計算を実行することで、FE_UNDERFLOW の発生を回避できます。


アンダーフローが発生すると、計算結果が 0 または最小正数値に変換される可能性があります。これは、計算精度を失うだけでなく、予期しない結果につながる可能性があるため、注意が必要です。

FE_UNDERFLOW を処理するには、以下の方法があります。

計算精度を変更する

より高い精度で計算を実行することで、FE_UNDERFLOW の発生を回避できます。具体的には、以下の方法があります。

  • printf 関数のフォーマット指定子を変更する
    printf 関数のフォーマット指定子で精度を指定することで、出力結果の精度を上げることができます。
  • double 型ではなく long double 型を使用する
    long double 型は double 型よりも精度が高いため、FE_UNDERFLOW の発生を回避できます。

以下のコード例は、double 型ではなく long double 型を使用して計算を行う方法を示しています。

#include <stdio.h>

int main() {
  long double x = 1.0 / 1000000000.0;

  printf("x = %Le\n", x);

  return 0;
}
x = 1.0000000000000000e-08

計算結果を丸め処理する

計算結果を丸め処理することで、0 または最小正数値に変換されるのを防ぐことができます。具体的には、以下の方法があります。

  • ceilf 関数を使用する
    ceilf 関数は、浮動小数点数を上に丸めます。
  • floorf 関数を使用する
    floorf 関数は、浮動小数点数を下に丸めます。
  • roundf 関数を使用する
    roundf 関数は、浮動小数点数を最寄りの整数に丸めます。

以下のコード例は、roundf 関数を使用して計算結果を丸め処理する方法を示しています。

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

int main() {
  float x = 1.0f / 1000000000.0f;

  x = roundf(x);

  printf("x = %e\n", x);

  if (fetestexcept(FE_UNDERFLOW)) {
    printf("アンダーフローが発生しました\n");
  } else {
    printf("アンダーフローは発生しませんでした\n");
  }

  return 0;
}
x = 0.000000e+00
アンダーフローは発生しませんでした

例外ハンドラを使用する

FE_UNDERFLOW が発生した場合に実行されるコードを記述できます。具体的には、以下の方法があります。

  • try-catch ブロックを使用して、例外処理を行います。
  • setjmp 関数と longjmp 関数を使用して、例外ハンドラにジャンプします。

以下のコード例は、setjmp 関数と longjmp 関数を使用して例外ハンドラにジャンプする方法を示しています。

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

jmp_buf jmpbuf;

void exception_handler() {
  printf("アンダーフローが発生しました\n");
  longjmp(jmpbuf, 1);
}

int main() {
  int result = setjmp(jmpbuf);

  if (result == 0) {
    float x = 1.0f / 1000000000.0f;

    printf("x = %e\n", x);
  } else {
    // 例外処理
  }

  return 0;
}
x = 0.000000e+00
アンダーフローが発生しました