"floating_point.c": Comparing the floating point numbers in C
hacker emblem Happy Hacking!

English Here (machine translation)

floating_point.c: IEEE754 浮動小数点数を比較する方式

この C 言語ユーティリティは、IEEE754 浮動小数点数 (double 型と long double 型) を整数 (long long あるいは long 配列) と見なした場合に「どれだけの差」があれば「等しい」と解釈するかというパラメータ (max-ullps) を定義するという方式を基礎にして、浮動小数点数同士を比較することができます。
このような API を作成したのは、C 言語の比較演算子で浮動小数点数同士を直接比較してしまうとイロイロと厄介な問題を惹き起こすので、アプリケーションの要求精度を満足させる「比較結果の正しさ」をパラメータ化したいと考えたからです。
この方式のベースとなる考え方の詳細については、"Comparing floating point numbers" by Bruce Dawson で述べられています。

比較だけでなく、非数 (Not a Number)無限大数負数の判定関数や、ゼロに最も近い浮動小数点数文字列変換バッファに必要な最小サイズの取得関数、さらに浮動小数点数の文字列変換関数も提供しています。

この API を使用するには、まずAPI の初期化を行う必要があります。

API を使用するためのヘッダは floating_point.h で、API の実装は floating_point.c です。API の説明は、この文書で後述しています。

テスト・プログラムとして test.bash も記述しました。これらのソースコードをコンパイルするために Makefile を使用できます。ライセンスは GNU Public License とします。


API の説明

  1. API を初期化する: init_floating_point_lib(), init_long_floating_point_lib()
    int init_floating_point_lib(int verbose
    , long long max_ullps
    )
    int init_floating_point_lib(int verbose, long *max_ullps)
    int init_long_floating_point_lib(int verbose
    , long *max_ullps
    )

    init_floating_point_lib() はこの API を使用するための初期化関数です。max_ullps には浮動小数点数を整数と見なした場合に等しいと解釈するべき「差」を正数で指定します。verbose にゼロ以外の値を渡すと、API から標準出力に処理結果などを表すメッセージが出力されます。

    max_ullps が long ポインタ型となっているものは、long long 型をサポートしていないコンパイラに NO_LONG_LONG マクロを指定すると有効になる関数プロトタイピングです。この場合、long long 型を代替するために long[LLO_CNT] 配列のアドレスを指定することになります。
    init_long_floating_point_lib() は long double 型をサポートするコンパイラに LONG_DOUBLE マクロを指定すると有効となる API です。この API には max_ullps として long[LLO__LONG_DOUBLE_CNT] 配列のアドレスを渡します。

    API の初期化に成功するとゼロを返します。エラーが発生した場合は非ゼロを返し errno にエラーの詳細を出力します。

    エラーが起きた際の errno の値とその意味は以下のとおりです。

    • EINVAL (22) - 渡された引数が不正です

  2. 浮動小数点数が max_ullps の範囲で等しいことを判定する: equals_doubles(), equals_long_doubles()
    int equals_doubles(double d1, double d2)
    int equals_long_doubles(long double d1, long double d2)

    equals_doubles() は二つの浮動小数点数 d1 と d2 が、API 初期化時に指定された max_ullps の範囲で「等しい」ことを判定します。

    equals_long_doubles() は long double 型をサポートするコンパイラに LONG_DOUBLE マクロを指定すると有効となる API です。この API は long double 型の浮動小数点数に対して使用します。

    二つの浮動小数点数が「等しい」場合は非ゼロを返します。「等しくない」場合にはゼロを返し errno にエラーの詳細を出力します。

    エラーが起きた際の errno の値とその意味は以下のとおりです。

    • EINVAL (22) - 渡された引数が不正です
    • ERANGE (34) - 浮動小数点数が「非数 not a number」です

  3. 浮動小数点数が max_ullps の範囲で等しいことを踏まえた比較を行う: cmp_double(), cmp_long_double()
    int cmp_double(double d1, double d2)
    int cmp_long_double(long double d1, long double d2)

    cmp_double() は二つの浮動小数点数 d1 と d2 が、API 初期化時に指定された max_ullps の範囲で「等しい」かあるいは大小関係のいずれにあるかを判定します。

    cmp_long_double() は long double 型をサポートするコンパイラに LONG_DOUBLE マクロを指定すると有効となる API です。この API は long double 型の浮動小数点数に対して使用します。

    二つの浮動小数点数 d1 と d2 が「等しい」場合はゼロを返します。d1 が d2 より小さい (d1 < d2) 場合は負数を返し、d1 が d2 より大きい (d1 > d2) 場合は正数を返します。
    エラーが発生した場合、戻り値にかかわらず、errno にエラー詳細を出力しますので注意してください。正常に比較が行えた場合、errno にゼロが出力されます。

    エラーが起きた際の errno の値とその意味は以下のとおりです。

    • EINVAL (22) - 渡された引数が不正です
    • ERANGE (34) - 浮動小数点数が「非数 not a number」です

  4. max_ullps を設定する: set_max_ullps(), set_max_long_ullps()
    int set_max_ullps(long long max_ullps)
    int set_max_ullps(long *max_ullps)
    int set_max_long_ullps(long *max_ullps)

    set_max_ullps() は二つの浮動小数点数の「等しさ」を定義付けるパラメータ max_ullps を設定します。プログラム実行の途中で max_ullps を変更する場合に使用することを想定しています。

    max_ullps が long ポインタ型となっているものは、long long 型をサポートしていないコンパイラで NO_LONG_LONG を指定すると有効になる関数プロトタイピングです。この場合、long long 型を代替するために long[LLO_CNT] 配列のアドレスを指定することになります。
    set_max_long_ullps() は long double 型をサポートするコンパイラに LONG_DOUBLE マクロを指定すると有効となる API です。この API の max_ullps には long[LLO_LONG_DOUBLE_CNT] 配列のアドレスを指定することになります。

    max_ullps を設定できた場合はゼロを返します。エラーが発生した場合は非ゼロを返し errno にエラーの詳細を出力します。

    エラーが起きた際の errno の値とその意味は以下のとおりです。

    • EINVAL (22) - 渡された引数が不正です

  5. ゼロに最も近い浮動小数点数を取得する: get_smallest(), get_long_smallest()
    double get_smallest(void)
    long double get_long_smallest(void)

    get_smallest() は浮動小数点数で表すことができる最もゼロに近い数を返します。

    get_long_smallest() は long double 型をサポートするコンパイラに LONG_DOUBLE マクロを指定すると有効となる API です。この API は long double 型でゼロに最も近い数を返します。

  6. 浮動小数点数が非数 (Not a Number) であることを判定する: is_not_a_number(), is_not_a_long_number()
    int is_not_a_number(double d)
    int is_not_a_long_number(long double d)

    is_not_a_number() は浮動小数点数が非数 (NaN: not a number) であるかどうかを判定します。

    is_not_a_long_number() は long double 型をサポートするコンパイラに LONG_DOUBLE マクロを指定すると有効となる API です。この API は long double 型の非数判定を行います。

    浮動小数点数 d が非数であれば非ゼロを返し、通常の数であればゼロを返します。

  7. 浮動小数点数が無限大数であることを判定する: is_infinite(), is_long_infinite()
    int is_infinite(double d)
    int is_long_infinite(long double d)

    is_infinite() は浮動小数点数が無限大数であるかどうかを判定します。

    is_long_infinite() は long double 型をサポートするコンパイラに LONG_DOUBLE マクロを指定すると有効となる API です。この API は long double 型の無限大数であることの判定を行います。

    浮動小数点数 d が無限大数であれば非ゼロを返し、通常の数であればゼロを返します。

  8. 浮動小数点数が負数であることを判定する: is_negative(), is_long_negative()
    int is_negative(double d)
    int is_long_negative(long double d)

    is_negative() は浮動小数点数が負数であるかどうかを判定します。

    is_long_negative() は long double 型をサポートするコンパイラに LONG_DOUBLE マクロを指定すると有効となる API です。この API は long double 型の負数であることの判定を行います。

    浮動小数点数 d が負数であれば非ゼロを返し、通常の数であればゼロを返します。

  9. 浮動小数点数を文字列変換するバッファに必要な最小サイズを取得する:
    get_min_size_of_double_string_buf(), get_min_size_of_long_double_string_buf()

    size_t get_min_size_of_double_string_buf(void)
    size_t get_min_size_of_long_double_string_buf(void)

    get_min_size_of_double_string_buf() は浮動小数点数を文字列に変換する場合に必要となる文字列バッファの最小サイズを返します。

    get_min_size_of_long_double_string_buf() は long double 型をサポートするコンパイラに LONG_DOUBLE マクロを指定すると有効となる API です。この API は long double 型の数を文字列変換する場合に必要なバッファの最小サイズを返します。

  10. 文字列を浮動小数点数に変換する: string2double(), string2long_double()
    double string2double(const char *s)
    long double string2long_double(const char *s)

    string2double() は文字列を浮動小数点数に変換します。

    string2long_double() は long double 型をサポートするコンパイラに LONG_DOUBLE マクロを指定すると有効となる API です。この API は文字列を long double 型の数に変換します。

    通常の浮動小数点数に変換できなかった場合は、errno にエラーの詳細を出力します。

    エラーが起きた際の errno の値とその意味は以下のとおりです。

    • EINVAL (22) - 渡された引数が不正です
    • ERANGE (34) - 非数 (NaN)、桁不足によるゼロへの縮退 (数値表現を満たしていない文字列 s の場合も含む) のいずれかが起きました