python/cpython · error · ValueError

Cannot convert signaling NaN to float

Error message

Cannot convert signaling NaN to float

What it means

float(Decimal('sNaN')) raises ValueError because a signaling NaN must trap on use, and conversion to float counts as a use; converting it would silently demote it to a quiet float NaN and lose the signal. Quiet NaNs convert fine to float('nan') with sign preserved.

Source

Thrown at Lib/_pydecimal.py:1570

                return context._raise_error(DivisionByZero, 'x // 0',
                                            self._sign ^ other._sign)
            else:
                return context._raise_error(DivisionUndefined, '0 // 0')

        return self._divide(other, context)[0]

    def __rfloordiv__(self, other, context=None):
        """Swaps self/other and returns __floordiv__."""
        other = _convert_other(other)
        if other is NotImplemented:
            return other
        return other.__floordiv__(self, context=context)

    def __float__(self):
        """Float representation."""
        if self._isnan():
            if self.is_snan():
                raise ValueError("Cannot convert signaling NaN to float")
            s = "-nan" if self._sign else "nan"
        else:
            s = str(self)
        return float(s)

    def __int__(self):
        """Converts self to an int, truncating if necessary."""
        if self._is_special:
            if self._isnan():
                raise ValueError("Cannot convert NaN to integer")
            elif self._isinfinity():
                raise OverflowError("Cannot convert infinity to integer")
        s = (-1)**self._sign
        if self._exp >= 0:
            return s*int(self._int)*10**self._exp
        else:
            return s*int(self._int[:self._exp] or '0')

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Quiet the sNaN first if loss of signal is acceptable: q = d + 0 under a context with InvalidOperation untrapped yields quiet NaN, then float(q)
  2. Reject sNaN explicitly at the boundary with is_snan() and raise your own error
  3. Keep the pipeline in Decimal end-to-end so no float() conversion is needed

Example fix

// before
f = float(dec)  # ValueError if dec is Decimal('sNaN')

// after
import math
f = math.nan if dec.is_snan() else float(dec)
Defensive patterns

Strategy: validation

Validate before calling

import math

def safe_float(d):
    if d.is_snan():
        return math.nan  # explicit policy: quiet it
    return float(d)

Type guard

from decimal import Decimal

def is_float_safe(d) -> bool:
    return not (isinstance(d, Decimal) and d.is_snan())

Try / catch

try:
    f = float(d)
except ValueError as e:
    if 'signaling NaN' in str(e):
        f = float('nan')
    else:
        raise

Prevention

When it happens

Trigger: float(Decimal('sNaN')); float(Decimal('sNaN123')); math.sin(Decimal('sNaN')) (implicit float conversion); json serialization code that calls float() on values.

Common situations: Parsed FIX/financial data carrying sNaN sentinels, then passed to float-based APIs (math, numpy, json); mixing decimal strictness with float-land helpers.

Related errors


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