
2026/10/11 0:39
標準Cにおいて、文字列から浮動小数点への変換エラーを検出するポータブルな方法は存在しない
RSS: https://news.ycombinator.com/rss
要約▶
日本語翻訳:
既存の要約は明確、正確かつ簡潔であり、改善の余地はありません。
テキストに翻訳する
(必要に応じて;そうでなければ原文を繰り返す):既存の要約は明確、正確かつ簡潔であり、改善の余地はありません。
本文
strtod
などの文字列変換関数におけるエラー処理の仕組みと実装依存性
strtodこの記事は、数学関数のエラー処理マクロ
math_errhandling および C 標準規格と POSIX の規定を補完する続編です。文字列から浮動小数点数への変換を行う一連の関数(strtod など)のエラー検出方法について解説します。
対象となる関数群
math.h に含まれていないが、同様に math_errhandling の影響を受ける変換関数は以下の通りです。
strtodstrtofstrtoldstrtod32strtod64strtod128
C 標準規格(7.25.2.6p12)での規定と実装依存性
規格では、オーバーフローやアンダーフロー時の挙動について以下のように記述されています。
オーバーフロー時
- 返値:
,HUGE_VAL
, またはHUGE_VALF
を返します(符号に依存)。HUGE_VALL - エラー報告条件:
が真の場合:整数式math_errhandling & MATH_ERRNO
はerrno
に設定される。ERANGE
が真の場合:浮動小数点例外として「オーバーフロー」が発生する。math_errhandling & MATH_ERREXCEPT
アンダーフロー時
- 返値: 最小正規化された正の数の絶対値以上にならないような値を返す。
- エラー報告条件(実装依存):
が真の場合:math_errhandling & MATH_ERRNO
をerrno
に設定するかどうかは実装依存。ERANGE
が真の場合:「アンダーフロー」という例外が発生するかどうかは実装依存。math_errhandling & MATH_ERREXCEPT
規格の意図と注意点
- musl 等のケース:
がmath_errhandling
のサポートを表明していない実装では、エラー時にerrno
は設定されません。errno - アンダーフロー報告義務なし: アンダーフロー時でもエラー報告が義務付けられていない点に注意が必要です。
POSIX 規格との違いと矛盾点
POSIX 規格は C 標準よりも厳格な要件を課しており、
math_errhandling に配慮していません。
POSIX の規定(過剰指定)
- オーバーフローまたはアンダーフロー発生時:必ず
をerrno
に設定する。[ERANGE] - この振る舞いは C 標準の「実装依存」という旨意に反しています。
man ページとの矛盾
Linux の man ページ(
strtod(3))はこの拡張機能は一切言及していません。これは POSIX の規定が文書化されていないためです。
errno
に関する規格条文(7.12.2p8, 7.5p3)との整合性
errno- 7.12.2p8:
がゼロの場合、math_errhandling & MATH_ERRNO
は設定されるか変更されない。errno - 7.5p3: 関数の説明で
の使用が言及されていない場合は例外となる。errno
矛盾の所在:
の説明にはstrtod
の使用が言及されています(ただしerrno
を条件としていません)。math_errhandling- これにより、POSIX の「常に
を設定する」という振る舞いは C 標準に準拠していない可能性が高まります。ERANGE - 実態の例:
- glibc: エラー時であっても
をerrno
に決して設定しない(返値はEINVAL
)。0 - musl: 変換不可能な場合、
をerrno
に設定する。EINVAL - 結論: man ページ上では文書化されていませんが、実装によって結果が異なります。
- glibc: エラー時であっても
エラー検出の「正しい」アプローチ
規格の記述が不明瞭であり、
errno だけでエラーを検出するのは非移植的です。以下にカテゴリ別に推奨される手法をまとめます。
1. オーバーフローの検出
POSIX をターゲットとする場合
の信頼度が高いため、シンプルにチェックします。errnoerrno = 0; // 呼び出し前のクリーンアップ double x = strtod(s, nullptr); bool overflow = (copysign(x, 1.0) == HUGE_VAL) && (errno == ERANGE);
それ以外(C 標準準拠)の場合
に従って検出メカニズムを判定する必要があります。math_errhandlingerrno = 0; feclearexcept(FE_OVERFLOW); // フラグをクリアする double x = strtod(s, nullptr); if (copysign(x, 1.0) == HUGE_VAL) { bool errored; if (math_errhandling & MATH_ERRNO) { errored = (errno == ERANGE); } else { // FENV_ACCESS を有効化しているか確認する(#pragma やコンパイラフラグ) errored = fetestexcept(FE_OVERFLOW); } }- 注意:
/feclearexcept
を使用する際は、コンパイラ設定(GCC:fetestexcept
または-ftrapping-math
)が必要な場合があります。#pragma STDC FENV_ACCESS ON
2. アンダーフローの検出
POSIX をターゲットとする場合
- 値がゼロであり且つ
がerrno
の場合に検出します。単にERANGE
とだけチェックすると非移植的です。errno > 0errno = 0; double x = strtod(s, nullptr); bool underflow = (x == 0.0) && (errno == ERANGE);
それ以外の場合
- 実装がアンダーフローを報告しているか事前に確認する必要があります(多くの場合、文書化されていないため困難)。
- SOL (Solution):
の関数ではチェックできず、入力文字列自体を確認する必要がある場合があります。libc// アンダーフローが発生したと推測される場合(例:結果が 0.0) // 入力文字列の指数部以前に非ゼロの数字があるかを手動で確認し境界条件をチェック if (result == 0.0) { // 入力文字列 "1e-500" のようなパターンを検出するロジックが必要 }
3. 無効な入力(変換なし)の検出
errno は信頼できません。必ず第 2 引数 endptr を使用し、ポインタが移動していないかチェックします。
char *endptr; double x = strtod(s, &endptr); if (endptr == s) { // 何も解析されなかった(入力文字列が無効または空) } else { // 変換が成功した // ※この時点で errno はエラーコードではなく、範囲外エラーなどを含みうるため注意 }
まとめ
:math_errhandling
の数学関数でエラーハンドリング機構(math.h
vs 例外)を示すマクロ。errno- 変換関数の特殊性:
群はstrtod
に依存せず、実装ごとに挙動が異なる可能性が高い。math_errhandling - POSIX の限界: POSIX は常に
を設定すると規定するが、glibc など多くの実装ではこれを無視している。errno - 推奨事項: 最大限の可搬性を確保するには、オーバーフロー/アンダーフローは例外/API 仕様を、無効入力 (
) はstrtod
を用いて検出することを徹底してください。endptr