[PATCH] math: Order signed zeros in fmin and fmax.

Wilco Dijkstra Wilco.Dijkstra@arm.com
Tue Dec 23 17:06:35 GMT 2025


Hi Adhemerval/James,

>> I believe the patch results in the desired behavior on all targets,
>> even though it modifies only the i386, i686, x86_64, and the generic C
>> implementation.

That's good to hear, it would be useful to mention something like that in the
description.

> As a side note, I don't think there is much gain is having the i386 version.
> If you a felling adventurous, maybe you can send a patch to just remove the
> i386 variants and making the i686 the default one.

I think the same is true for the x86_64 version, with my proposed improvement
below, it generates:

"__fmin":
        ucomisd xmm0, xmm1
        movapd  xmm2, xmm0
        jp      .L14
        je      .L29
        minsd   xmm2, xmm1
        movapd  xmm0, xmm2
        ret

This is from writing fmin/fmax like this:

  if (!isunordered (x, y))
  {
    if (__glibc_unlikely (x == y))
      return signbit (x) ? x : y;
    return x < y ? x : y;
  }

This has fewer operations and branches, and allows targets to use conditional moves
or min/max in both return cases.

The only remaining issue with the generic C implementation is that we don't yet
inline issignaling, so the NaN handling is slightly slower. If that is inlined then the
i686 assembler implementation could likely be removed too.

> The additional targets in glibc which don't use the generic C
> implementation are:
> - riscv
> - loongarch (via setting USE_F{MIN,MAX}{,F}_BUILTIN)
> - aarch64 (via setting USE_F{MIN,MAX}{,F}_BUILTIN)
>
> On these architectures, the operation is already implemented by a
> single hardware instruction with the desired behavior; no changes are
> required.

There are more, eg. power will emit "xsmindp" since power7 at least, but they don't
seem to define USE_F{MIN,MAX}{,F}_BUILTIN.

>> There are at least
>> 3 different variations of fmin/fmax in the IEEE standard, so I'm wondering whether
>> they do agree on how to handle signed zeroes... If it works on all modern targets,
>> that's great, but that's not clear from your description because ISAs typically
>> follow IEEE specs.
>
> Yes, the other variations of floating-point min/max are fminimum,
> fminimum_mag, fminimum_num, fminimum_mag_num (and their corresponding
> maximums). Those functions are required to order signed zeros by the C
> standard, unlike fmin/fmax where it is only recommended.

fminmag/fmaxmag currently don't order signed zeroes either:

FLOAT
M_DECL_FUNC (__fminmag) (FLOAT x, FLOAT y)
{
  FLOAT ax = M_FABS (x);
  FLOAT ay = M_FABS (y);
  if (isless (ax, ay))
    return x;
  else if (isgreater (ax, ay))
    return y;
  else if (ax == ay)
    return x < y ? x : y;
  else if (issignaling (x) || issignaling (y))
    return x + y;
  else
    return isnan (y) ? x : y;
}

This one does do it, but inefficiently (there is also different NaN handling...):

FLOAT
M_DECL_FUNC (__fminimum_mag_num) (FLOAT x, FLOAT y)
{
  FLOAT ax = M_FABS (x);
  FLOAT ay = M_FABS (y);
  if (isless (ax, ay))
    return x;
  else if (isgreater (ax, ay))
    return y;
  else if (ax == ay)
    return (M_COPYSIGN (1, x) <= M_COPYSIGN (1, y) ? x : y);
  else
    return isnan (y) ? (isnan (x) ? x + y : x) : y;
}

So it would be great to make them consistent for signed zero and use the same efficient
sequence in all cases.

Cheers,
Wilco


More information about the Libc-alpha mailing list