python/cpython · error · ValueError

cannot convert NaN to integer ratio

Error message

cannot convert NaN to integer ratio

What it means

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.

Source

Thrown at Lib/_pydecimal.py:948

        return DecimalTuple(self._sign, tuple(map(int, self._int)), self._exp)

    def as_integer_ratio(self):
        """Express a finite Decimal instance in the form n / d.

        Returns a pair (n, d) of integers.  When called on an infinity
        or NaN, raises OverflowError or ValueError respectively.

        >>> Decimal('3.14').as_integer_ratio()
        (157, 50)
        >>> Decimal('-123e5').as_integer_ratio()
        (-12300000, 1)
        >>> Decimal('0.00').as_integer_ratio()
        (0, 1)

        """
        if self._is_special:
            if self.is_nan():
                raise ValueError("cannot convert NaN to integer ratio")
            else:
                raise OverflowError("cannot convert Infinity to integer ratio")

        if not self:
            return 0, 1

        # Find n, d in lowest terms such that abs(self) == n / d;
        # we'll deal with the sign later.
        n = int(self._int)
        if self._exp >= 0:
            # self is an integer.
            n, d = n * 10**self._exp, 1
        else:
            # Find d2, d5 such that abs(self) = n / (2**d2 * 5**d5).
            d5 = -self._exp
            while d5 > 0 and n % 5 == 0:
                n //= 5
                d5 -= 1

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Guard with self.is_nan() / value._is_special before calling (see defense code)
  2. Decide a policy for NaN: skip the record, substitute 0, or propagate an explicit error to the caller
  3. Validate data at ingestion so NaN sentinels never reach numeric conversion code

Example fix

// before
n, d = dec.as_integer_ratio()  # ValueError on NaN

// after
if dec._is_special:
    raise ValueError(f'non-finite input: {dec}')
n, d = dec.as_integer_ratio()
Defensive patterns

Strategy: validation

Validate before calling

def safe_ratio(d):
    if d._is_special:
        raise ValueError(f'non-finite decimal: {d}')
    return d.as_integer_ratio()

Type guard

def has_integer_ratio(d) -> bool:
    return not d._is_special

Try / catch

try:
    n, d_ = dec.as_integer_ratio()
except ValueError:
    handle_nan(dec)  # NaN input path
except OverflowError:
    handle_inf(dec)  # Infinity input path

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/073156b38001b578. Report an issue: GitHub.