Skip to content

Conversation

SwayamInSync
Copy link
Member

This PR adds the nextafter ufunc support to quaddtype. I referenced the gcc's libquadmath's implementation for __float128 and tried to map it back in SLEEF.

For testing we can't do the normal value comparison against numpy with other dtype like float64 so I added checking the properties like

  • nextafter(1,2) => eps
  • nextafter(0, 1) => subnormal
  • nextafter(+inf, finite) => max_finfite
    and similar more checking symmetry and direction. I also tested the C implementation against the libquadmath and it seems to be passing there as well.

@SwayamInSync
Copy link
Member Author

Tagging @seberg @ngoldbaum @juntyr please review this one. I also check the NumPy's implementation but in the comments seem to mention faults there so I skipped it.


// NaN if either is NaN
if (Sleef_iunordq1(*x, *y)) {
return Sleef_addq1_u05(*x, *y); // still NaN
Copy link
Member Author

Choose a reason for hiding this comment

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

We can also just return the NaN here but libquadmath's version was doing addition to I did that too just to keep the consistency between implementtion

The thing is addition will also return NaN but the internal bit representation would be changed. Its a call of choice I think, if you all prefer this we can keep it.

@juntyr
Copy link
Contributor

juntyr commented Oct 19, 2025

What does nextafter(-0.0, 1.0) and nextafter(+0.0, -1.0) do?

@juntyr
Copy link
Contributor

juntyr commented Oct 19, 2025

Spelling error (but for some reason I cannot comment inline right now): "did not need the checks" (at the final sleef return)

@juntyr
Copy link
Contributor

juntyr commented Oct 19, 2025

Once we also have an implementation of the spacing ufunc, both ufuncs should have a test using the other ufunc

@juntyr
Copy link
Contributor

juntyr commented Oct 19, 2025

Thanks @SwayamInSync! You have done so much fantastic work over the past few days!

@SwayamInSync
Copy link
Member Author

SwayamInSync commented Oct 19, 2025

What does nextafter(-0.0, 1.0) and nextafter(+0.0, -1.0) do?

nextafter(-0, 1) => (+) smallest subnormal
nextafter(+0, -1) => (-) subnormal (can't call it smallest as logically it is the biggest negative value representable)

Its like 1st argument defines where you are and 2nd in which direction (not value) you want to go and returns the very next representable floating value

Spelling error (but for some reason I cannot comment inline right now): "did not need the checks" (at the final sleef return)

Ah sorry, will fix that

@SwayamInSync
Copy link
Member Author

Once we also have an implementation of the spacing ufunc, both ufuncs should have a test using the other ufunc

For spacing I was thinking we can just do

nextafter(x, inf) - x

@seberg
Copy link
Member

seberg commented Oct 20, 2025

Spacing, you may have to be careful about the sign of x, but not sure if NumPy proper does that right. Both since spacing is presumably always absolute and because the larger spacing is away from zero.

@SwayamInSync
Copy link
Member Author

Spacing, you may have to be careful about the sign of x, but not sure if NumPy proper does that right. Both since spacing is presumably always absolute and because the larger spacing is away from zero.

In numpy

spacing(1.0) => eps
spacing(-1.0) => -eps

sign depends on the values, I am not sure whether this is the right behaviour, as from the docs which mention

Return the distance between x and the nearest adjacent number.

Distance should always be positive. So what you recommend? following NumPy's behaviour or change it to always return positive (this will also require to be fix from NumPy side then)

@seberg
Copy link
Member

seberg commented Oct 20, 2025

Yeah, I thought I faintly remembered there was something weird about it :(. So it uses the right distance (the larger one), but returns a signed value...

While it seems to me that an absolute value makes more sense, it is very unclear to me if someone might be relying on it being signed!? The docs also feel slightly unclear, since it is actually not the nearest adjacent number, it is the second nearest (and that is good).
(I suspect all of this basically just means that few use this function and everyone just uses the eps value in practice.)

In the end... I guess you may want to just follow NumPy in its current behavior and update the docs :/. I would like to change it, but I am not sure it is worthwhile, unless we do it by renaming the fundction or so...

Copy link
Contributor

@juntyr juntyr left a comment

Choose a reason for hiding this comment

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

LGTM

@SwayamInSync
Copy link
Member Author

Cool taking this in!

@SwayamInSync SwayamInSync merged commit 50f3fd3 into numpy:main Oct 21, 2025
10 checks passed
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