RustPython/RustPython · error · ValueError

cannot round a NaN

Error message

cannot round a NaN

What it means

The one-argument round(Decimal) rescales to an integer with ROUND_HALF_EVEN, which is undefined for specials: a NaN raises this ValueError (infinity raises OverflowError). Note round(sNaN, 0) instead routes through quantize and returns a NaN with the diagnostic attached.

Source

Thrown at Lib/_pydecimal.py:1840

        >>> round(Decimal('123.456'), -2)
        Decimal('1E+2')
        >>> round(Decimal('-Infinity'), 37)
        Decimal('NaN')
        >>> round(Decimal('sNaN123'), 0)
        Decimal('NaN123')

        """
        if n is not None:
            # two-argument form: use the equivalent quantize call
            if not isinstance(n, int):
                raise TypeError('Second argument to round should be integral')
            exp = _dec_from_triple(0, '1', -n)
            return self.quantize(exp)

        # one-argument form
        if self._is_special:
            if self.is_nan():
                raise ValueError("cannot round a NaN")
            else:
                raise OverflowError("cannot round an infinity")
        return int(self._rescale(0, ROUND_HALF_EVEN))

    def __floor__(self):
        """Return the floor of self, as an integer.

        For a finite Decimal instance self, return the greatest
        integer n such that n <= self.  If self is infinite or a NaN
        then a Python exception is raised.

        """
        if self._is_special:
            if self.is_nan():
                raise ValueError("cannot round a NaN")
            else:
                raise OverflowError("cannot round an infinity")
        return int(self._rescale(0, ROUND_FLOOR))

View on GitHub (pinned to aaeab4f754)

Solutions

  1. Gate on finiteness: r = round(d) if d.is_finite() else None.
  2. Reject NaN tokens at parse time so rounding never sees them.
  3. Catch ValueError around round() for untrusted input.

Example fix

# before
r = round(Decimal(value_str))  # ValueError when value_str == 'nan'

# after
d = Decimal(value_str)
r = round(d) if d.is_finite() else None
Defensive patterns

Strategy: validation

Validate before calling

r = round(d) if d.is_finite() else None

Type guard

def roundable_decimal(d):
    return d.is_finite()

Try / catch

try:
    r = round(d)
except ValueError:
    r = None  # NaN policy

Prevention

When it happens

Trigger: round(Decimal('NaN')); round(Decimal('sNaN')); rounding values parsed from strings like 'nan' or produced by 0/0-style operations under non-trapping contexts.

Common situations: Rounding user-supplied numeric fields where NaN is a valid token; processing sensor/datafeed NaN sentinels through a rounding step.

Related errors


AI-assisted analysis of RustPython/RustPython@aaeab4f754 (2026-08-17). Data as JSON: /api/errors/ab16a2c3e5edb14c. Report an issue: GitHub.