Skip to content

Reject non-integral float-like values (numpy floats) for integer parameters - #146

Merged
jhonabreul merged 2 commits into
QuantConnect:masterfrom
jhonabreul:bug-numpy-float-int-truncation
Aug 12, 2026
Merged

Reject non-integral float-like values (numpy floats) for integer parameters#146
jhonabreul merged 2 commits into
QuantConnect:masterfrom
jhonabreul:bug-numpy-float-int-truncation

Conversation

@jhonabreul

Copy link
Copy Markdown
Collaborator

What does this implement/fix? Explain your changes.

Passing a non-integral numpy float where a .NET integer parameter is expected silently truncates instead of raising: SimpleMovingAverage(np.float64(20.5)) builds a period-20 indicator, while plain float(20.5) is correctly rejected with a TypeError.

fb5cce6 (#128) added the non-integral rejection for Python floats, but the guard in Converter.ToPrimitive uses Runtime.PyFloat_Check, an exact type-pointer check. Two kinds of values bypass it and fall through to PyNumber_Long/__int__, which truncates:

  • float subclasses — numpy.float64 is a subclass of Python float
  • numbers that only define __float__numpy.float32/float16 are not float subclasses

This PR extends the guard to any float-like value:

  • a Python float including subclasses (PyObject_TypeCheck against PyFloatType), or
  • a number defining __float__ but not __index__ (Python's marker for losslessly-int-convertible types)

Integral-valued floats (np.float64(20.0)) keep converting, numpy integer scalars (np.int64, np.int32, which advertise __index__) are unaffected, and plain ints take a fast early exit. If a probed __float__ call fails, the pending Python error is cleared before rejecting, so no stale error leaks.

Behavior note: non-numpy numeric types with __float__/__int__ but no __index__ (e.g. decimal.Decimal, fractions.Fraction) previously also truncated silently into integer parameters (Decimal("20.5")20); with this change their non-integral values are rejected the same way, while integral values keep converting exactly as before.

Does this close any currently open issues?

No open issue; found while reproducing the fleet error-surface study (Agents#305, improvement 1).

Any other comments?

Tests:

  • TestFloatToIntConversion embed tests: new float-subclass, __float__-only and __index__ fixture cases over single and overloaded targets (22 pass).
  • tests/test_conversion.py::test_numpy_float_to_int_conversion: numpy-backed coverage (np.float64/np.float32 integral and non-integral, np.int32/np.int64, plain-float regression, method binding), skipped automatically when numpy is absent.
  • Full embed suite: 982 passed, 0 failed. Full python suite: 457 passed; the single failure (test_module.py::test_explicit_assembly_load) is a local-environment artifact and fails identically on clean master.

Checklist

Check all those that are applicable and complete.

  • Make sure to include one or more tests for your change
  • If an enhancement PR, please create docs and at best an example
  • Ensure you have signed the .NET Foundation CLA
  • Add yourself to AUTHORS
  • Updated the CHANGELOG

The non-integral rejection added in fb5cce6 only covered exact Python
floats: Runtime.PyFloat_Check compares the type pointer, so float
subclasses such as numpy.float64 and __float__-only numbers such as
numpy.float32 bypassed the guard in Converter.ToPrimitive and fell
through to PyNumber_Long/__int__, silently truncating the value (e.g.
SimpleMovingAverage(np.float64(20.5)) built a period-20 indicator).

Extend the guard to any float-like value: Python floats including
subclasses, and numbers that define __float__ but no __index__. True
integer types advertising __index__ (numpy.int64/int32) and plain ints
are unaffected, and integral-valued floats (20.0) keep converting.

Adds embed tests with float-subclass / __float__-only / __index__
fixtures and a numpy-backed python test over ConversionTest fields and
method binding.
@jhonabreul
jhonabreul marked this pull request as ready for review August 11, 2026 17:53
@jhonabreul
jhonabreul merged commit cc51794 into QuantConnect:master Aug 12, 2026
9 checks passed
@jhonabreul
jhonabreul deleted the bug-numpy-float-int-truncation branch August 12, 2026 20:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants