{"record":{"id":"073156b38001b578","repo":"python/cpython","slug":"cannot-convert-nan-to-integer-ratio","errorCode":null,"errorMessage":"cannot convert NaN to integer ratio","messagePattern":"cannot convert NaN to integer ratio","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/_pydecimal.py","lineNumber":948,"sourceCode":"        return DecimalTuple(self._sign, tuple(map(int, self._int)), self._exp)\n\n    def as_integer_ratio(self):\n        \"\"\"Express a finite Decimal instance in the form n / d.\n\n        Returns a pair (n, d) of integers.  When called on an infinity\n        or NaN, raises OverflowError or ValueError respectively.\n\n        >>> Decimal('3.14').as_integer_ratio()\n        (157, 50)\n        >>> Decimal('-123e5').as_integer_ratio()\n        (-12300000, 1)\n        >>> Decimal('0.00').as_integer_ratio()\n        (0, 1)\n\n        \"\"\"\n        if self._is_special:\n            if self.is_nan():\n                raise ValueError(\"cannot convert NaN to integer ratio\")\n            else:\n                raise OverflowError(\"cannot convert Infinity to integer ratio\")\n\n        if not self:\n            return 0, 1\n\n        # Find n, d in lowest terms such that abs(self) == n / d;\n        # we'll deal with the sign later.\n        n = int(self._int)\n        if self._exp >= 0:\n            # self is an integer.\n            n, d = n * 10**self._exp, 1\n        else:\n            # Find d2, d5 such that abs(self) = n / (2**d2 * 5**d5).\n            d5 = -self._exp\n            while d5 > 0 and n % 5 == 0:\n                n //= 5\n                d5 -= 1","sourceCodeStart":930,"sourceCodeEnd":966,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_pydecimal.py#L930-L966","documentation":"Decimal.as_integer_ratio() returns an exact (numerator, denominator) pair for finite decimals, but NaN has no numeric value, so it raises ValueError('cannot convert NaN to integer ratio'). Infinity hits the sibling OverflowError branch instead.","triggerScenarios":"Decimal('NaN').as_integer_ratio(); Decimal('-sNaN2').as_integer_ratio() (any NaN including signaling); values read from a feed that contained a NaN sentinel before conversion.","commonSituations":"Converting Decimal money/quantities to Fraction for exact rational math without pre-checking for specials; float(x).as_integer_ratio()-style code ported to Decimal where NaN can persist unparsed.","solutions":["Guard with self.is_nan() / value._is_special before calling (see defense code)","Decide a policy for NaN: skip the record, substitute 0, or propagate an explicit error to the caller","Validate data at ingestion so NaN sentinels never reach numeric conversion code"],"exampleFix":"// before\nn, d = dec.as_integer_ratio()  # ValueError on NaN\n\n// after\nif dec._is_special:\n    raise ValueError(f'non-finite input: {dec}')\nn, d = dec.as_integer_ratio()","handlingStrategy":"validation","validationCode":"def safe_ratio(d):\n    if d._is_special:\n        raise ValueError(f'non-finite decimal: {d}')\n    return d.as_integer_ratio()","typeGuard":"def has_integer_ratio(d) -> bool:\n    return not d._is_special","tryCatchPattern":"try:\n    n, d_ = dec.as_integer_ratio()\nexcept ValueError:\n    handle_nan(dec)  # NaN input path\nexcept OverflowError:\n    handle_inf(dec)  # Infinity input path","preventionTips":["Check _is_special (or is_nan()/is_infinite()) once for both special cases","Validate decimals are finite at ingestion before exact-ratio code","Catch ValueError and OverflowError separately if NaN vs Infinity need different handling"],"tags":["decimal","as-integer-ratio","nan","fraction","valueerror"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}