[PATCH 2/3] math: Optimize dbl-64 remainder implementation

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Fri Sep 12 13:21:56 GMT 2025



On 11/09/25 18:02, Wilco Dijkstra wrote:
> Hi Adhemerval,
> 
> +  uint64_t hx = asuint64 (x);
> +  uint64_t hy = asuint64 (y);
> +  uint64_t sx = hx & UINT64_C (0x8000000000000000);
> 
> We below change, could become sign = hx >> 63;
> 
> +  hx &= UINT64_C (0x7FFFFFFFFFFFFFFF);
> +  hy &= UINT64_C (0x7FFFFFFFFFFFFFFF);
> +
> +  /* |y| == 0 or |x| not finite or |y| is NaN */
> +  if (hy == 0
> +      || (hx >= EXPONENT_MASK || hy > EXPONENT_MASK))
> 
> Surely (hy - 1) >= EXPONENT_MASK to remove the hy == 0 check.

Ack.

> 
> +    return NAN;
> 
> Since NAN returns a float NaN, wouldn't C99 nan("") be better?

Right, I think it should ok and not create a nan call for all targets.

> 
> +  /* |y| < DBL/MAX / 2 ? */
> +  if (hy < UINT64_C (0x7fdfffffffffffff))
> +    x = __ieee754_fmod (x, y + y);
> 
> Can use hy <=, but hy < UINT64_C (0x7fe0000000000000) is a simpler constant
> on various targets.

Ack.

> 
> +  if ((hx - hy) == 0)
> +    return 0.0 * x;
> 
> Is this strictly required? I think the code below will also return 0.0 with sign of
> original x, or does the correction for rounding mode make it fail? Does
> remainder (x, x) return a different sign of zero than remainder (x + x , x)?

I think you correct, this check seems unnecessary and it does not trigger any
regressions.  I will remove from the float version as well.

> 
> If it is needed, placing if (hx == hy) before the fmod call would be better.
> 
> +  x = fabs (x);
> +  y = fabs (y);
> +
> +  /* |y| < 2 * DBL_MIN */
> +  if (hy < UINT64_C (0x20000000000000))
>      {
> +      if (x + x > y)
>          {
> +         x -= y;
> +         if (x + x >= y)
> +           x -= y;
>          }
>      }
> +  else
> +    {
> +      double y_half = y * 0.5;
> +      if (x > y_half)
> +       {
> +         x -= y;
> +         if (x >= y_half)
> +           x -= y;
> +       }
> +    }
> 
> Since these branches may be fairly hard to predict, it's worth checking whether
> this works out better:
> 
> x = (x >= y) ? x - y : x;
> x = ((x + x) >= y) ? x - y : x;
> 
> You can always start with x >= y and only use (x + x) or y * 0.5 for the 2nd part.

I did not see much difference neither on x86_64 (Ryzen 9) or aarch64 (N1) with
recent gcc.
> 
> In either case, we can remove the if (hy < UINT64_C (0x20000000000000)) by adding
> an else after the fmod to handle the case of large y.

I am not sure if we can remove it, since it to handle small y.

> 
> +  hx = asuint64 (x);
> +  /* Make sure x is not -0. This can occur only when x = p  and rounding
> +     direction is towards negative infinity.  */
> +  if (hx == UINT64_C (0x8000000000000000))
> +    hx = 0;
> +  return asdouble (hx ^ sx);
> 
> Isn't this equivalent to:
> 
> if (__glibc_unlikely (x == 0.0))
>   x = 0.0;
> return sx ? -x : x;
> 
> That avoids the float-int-float roundtrip and large constants, so that might be faster
> overall.

I think you mean x == -0.0 and yeah I think it should better indeed.

> 
> Cheers,
> Wilco



More information about the Libc-alpha mailing list