Skip to content

Conversation

@SwayamInSync
Copy link
Member

Copilot Summary

This pull request adds full support for the frexp function for quad-precision types in the NumPy extension, including backend implementation, ufunc integration, and comprehensive tests. The changes ensure that frexp behaves consistently with NumPy's standard types, correctly handling edge cases such as zero, infinity, and NaN.

Backend and API implementation:

  • Added quad_frexp and ld_frexp functions to ops.hpp, implementing correct mantissa and exponent extraction for quad-precision and long double, including handling of NaN, zero, and infinity inputs.
  • Defined new function pointer types frexp_op_quad_def and frexp_op_longdouble_def for backend selection of frexp operations.

NumPy ufunc integration:

  • Implemented frexp-specific descriptor resolver and strided loop functions for both aligned and unaligned memory in unary_ops.cpp, and registered the new ufunc for quad-precision types. [1] [2]

Testing and documentation:

  • Added a comprehensive TestFrexp class to test_quaddtype.py, covering a wide range of inputs and edge cases, and verifying consistency with NumPy's float64 results.
  • Updated release_tracker.md to mark frexp as fully supported for quad-precision types.

Other improvements:

  • Minor code comment update in binary_ops.cpp for clarity regarding binary ufuncs with two outputs.

@SwayamInSync
Copy link
Member Author

I expected it to fail on s390x but it passed that and failed on windows :)
Will fix it, issue is again integer handling

@SwayamInSync
Copy link
Member Author

These test failures are pretty weird.
np.frexp on float64 return exponent as -1 for NAN and inf but 0 on linux and macos machine.

Is this is expected? @seberg are we testing this properly in numpy?

Details ```

self = <test_quaddtype.TestFrexp object at 0x00000292F997C290>, x_val = 'inf'

  @pytest.mark.parametrize("x_val", [
      "inf",
      "-inf",
  ])
  def test_frexp_inf(self, x_val):
      """Test frexp with infinity (should return �inf mantissa, exponent 0)"""
      quad_x = QuadPrecision(x_val)
  
      quad_m, quad_e = np.frexp(quad_x)
  
      # Mantissa should be infinity with same sign
      assert np.isinf(quad_m), f"Mantissa should be infinity for frexp({x_val})"
      assert np.signbit(quad_m) == np.signbit(quad_x), \
          f"Sign mismatch for frexp({x_val}) mantissa"
  
      # Exponent should be 0
      assert quad_e == 0, f"Exponent should be 0 for frexp({x_val})"
  
      # Compare with NumPy float64
      float_x = np.float64(x_val)
      float_m, float_e = np.frexp(float_x)
  
      # Both should be infinity with same sign
      assert np.isinf(float_m), f"NumPy mantissa should also be infinity for frexp({x_val})"
      assert np.signbit(quad_m) == np.signbit(float_m), \
          f"Sign mismatch with NumPy for frexp({x_val})"
  
      # Exponent should match
  assert quad_e == float_e, \
          f"Exponent mismatch with NumPy for frexp({x_val}): {quad_e} != {float_e}"

E AssertionError: Exponent mismatch with NumPy for frexp(inf): 0 != -1
E assert np.int32(0) == np.int32(-1)

@seberg
Copy link
Member

seberg commented Oct 24, 2025

You should compare the C standard for this type of thing, e.g. here

If arg is NaN, NaN is returned, and an unspecified value is stored in *exp.

(similar for inf). So yes, seems fine, since I doubt that there is reason to normalize it beyond what the C standard says.

@SwayamInSync
Copy link
Member Author

Ah yes sorry, that make sense
I should've checked the reference. Thanks

return 0;
}

// Frexp-specific resolver: QuadPrecDType -> (QuadPrecDType, int32)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it work this time with int32 when we needed intp for the other direction?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The python takes input as PyArray_longtype but sleef functions take int32 as input. And during the input we have to copy the bytes from big to small that causes platform issues.

In the output numpy keeps it int32 so there is no misalignedness.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants