テクノロジー · 実用ガイド
Decimal.quantize が InvalidOperation?精度を変える前に結果の桁数を確認
Decimal の精度は小数点以下ではなく係数全体の桁数です。隔離した試験で繰り上がり、ゼロ埋め、非有限値を区別します。

quantize の InvalidOperation は入力の小数桁数ではなく、必要な結果の容量に関係する場合があります。精度を上げたりトラップを無効化したりする前に、演算対象と実際のコンテキストを保存してください。
1. 例外名だけでなく演算を記録する
完全なトレースバック、二つの Decimal オペランドと入手元を保存します。失敗箇所の repr(value)、value.as_tuple()、repr(quantum)、getcontext().prec、rounding、Emin、Emax、traps、既存の flags を記録してください。ライブラリーが別の context を明示的に渡していないかも確認します。別の場所で見た DefaultContext が実際の設定とは限りません。
構築と量子化を分けます。float から作った Decimal はその浮動小数点値を保持し、元の十進テキストを復元しません。入力契約が文字列なら元の文字列を優先します。不正な文字列、非対応の大きさ、後の quantize エラーを同じ精度設定で処理しないでください。
2. 繰り上がりとゼロ埋め後の桁数を数える
精度は小数点以下の桁数ではなく係数全体です。CPython 3.12.14 と ROUND_HALF_EVEN の試験では Decimal('9.995').quantize(Decimal('0.01')) は精度3で失敗し、精度4で10.00を返しました。結果には4桁必要です。整数1桁と小数2桁だけの予算では繰り上がりを見落とします。
Decimal('1.23').quantize(Decimal('0.0001')) は精度3で失敗し、精度5で1.2300を返しました。数値が同じでも必要な末尾ゼロは容量を使います。対応する大きさ、要求指数、繰り上がりから作業精度を決め、指数上下限も確認します。万能の精度値はありません。
3. 無効値を隠さず、変更を隔離する
有限値ポリシーを使う前に value.is_finite() と quantum.is_finite() を調べます。同じ試験では静かな NaN は例外なしで NaN へ伝わり、sNaN と Infinity を有限 quantum へ量子化すると InvalidOperation が出ました。例外を捕まえるだけでは非有限結果の受け入れを防げません。
InvalidOperation トラップを無効にすると、精度3の9.995の失敗は NaN と同名フラグに変わりました。10.00が収まったわけではありません。with localcontext() as ctx で範囲を限定し、確認した精度、丸め、トラップだけを設定します。外側を保持し、ライブラリー内部の設定変更は文書で確認してください。
4. 値・指数・シグナルで合格を判断する
局所コンテキストで監視対象の演算直前に flags を消し、直後に読みます。有限出力契約なら result.is_finite()、期待値、result.as_tuple().exponent と quantum の指数の一致を要求します。Inexact と Rounded を許すか明示してください。10.00では両方が立ち、1.2300へのゼロ埋めではどちらも立ちませんでした。例外なしと厳密な演算は別です。
通常入力、整数境界の繰り上がり、ゼロ埋め、負数、ゼロ、最大対応桁数、拒否対象の特殊値を実際のアプリ規則で再試験します。8件の合成試験は本文の分岐と外側の精度復元を確認しただけで、本番データ、財務規則、他社ライブラリーを検証していません。まだ失敗するなら対象、コンテキスト、フラグを保存し、その分岐を調べます。
