Re: max/min on nan arguments

Anton Idukov via Chicken-users <[email protected]> Sun, 2 Aug 2026 07:31:21 +0300
Newsgroups gmane.lisp.scheme.chicken
Message-ID <CAMJCnqXvXjEA-Jd+0+qe+yQosa4FDgg-G2UPo=8cmN5cfd++jA@mail.gmail.com>
--000000000000775e68065808e502
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

C side tends to align with more modern   ISO/IEC 60559:2020
https://www.iso.org/standard/80985.html  (which mentioned in
https://en.cppreference.com/c/numeric/math/fmax    for example) which
behavior is more likely "option 1"

=D0=B2=D1=81, 2 =D0=B0=D0=B2=D0=B3. 2026=E2=80=AF=D0=B3. =D0=B2 01:56, Pete=
r McGoron via Chicken-users <
[email protected]>:

> This is on the latest master.
>
>       (max +nan.0 1.0 2.0) =3D> +nan.0
>       (max 1.0 +nan.0 2.0) =3D> 2.0
>       (max 1.0 2.0 +nan.0) =3D> 2.0
>
> This looks like these procedures are using `<` and `>` to compare
> numbers, which fails on NaN.
>
> There are two IEEE ways to handle NaNs in max and min:
>
> 1. Ignore them when possible, and only return a NaN when all arguments
> are NaNs. So the above examples would be equivalent to `(max 1.0 2.0)`.
>
> 2. If any input arguments are NaN, then return a NaN. So each example
> above would return NaN.
>
> The R7RS doesn't specify any of these behaviors.
>
> On the implementations I've tested the examples on that didn't have
> inconsistent results, they all returned NaN when any input is a NaN. So
> I recommend the second option because it will cause more portable
> behavior across implementations.
>
> My suggestion is to replace `(if (> h m) h m)` in the definition of
> `max` with `(##max2 h m)`, where for the two options above:
>
> (define (##max2-option1 h m)
>    (cond
>      ((and (nan? h) (nan? m)) h)
>      ((nan? h) m)
>      ((nan? m) m)
>      ((and (eqv? h -0.0) (eqv? m +0.0)) m)
>      ((and (eqv? h +0.0) (eqv? m -0.0)) h)
>      ((< h m) m)
>      (else h)))
>
> (define (##max2-option2 h m)
>    (cond
>      ((nan? h) h)
>      ((nan? m) m)
>      ((and (eqv? h -0.0) (eqv? m +0.0)) m)
>      ((and (eqv? h +0.0) (eqv? m -0.0)) h)
>      ((< h m) m)
>      (else h)))
>
> IEEE 754-2019 mandates that, for the purposes of max and min, +0.0 is
> greater than -0.0. Similar things apply to min.
>
> I would add the following test cases:
>
>       (max +nan.0 1.0 2.0) =3D> 2.0 (option 1) OR +nan.0 (option 2)
>       (max 1.0 +nan.0 2.0) =3D> same
>       (max 1.0 2.0 +nan.0) =3D> same
>       (max +nan.0)         =3D> +nan.0
>       (max +nan.0 +nan.0)  =3D> +nan.0
>       (max -0.0 +0.0)      =3D> +0.0
>       (max +0.0 -0.0)      =3D> +0.0
>
>       (min +nan.0 1.0 2.0) =3D> 1.0 (option 1) OR +nan.0 (option 2)
>       (min 1.0 +nan.0 2.0) =3D> same
>       (min 1.0 2.0 +nan.0) =3D> same
>       (min +nan.0)         =3D> +nan.0
>       (min +nan.0 +nan.0)  =3D> +nan.0
>       (min -0.0 +0.0)      =3D> -0.0
>       (min +0.0 -0.0)      =3D> -0.0
>
> -- Peter McGoron
>
>

--000000000000775e68065808e502
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr">C side tends to align with more modern=C2=A0 =C2=A0ISO/IEC=
 60559:2020 <a href=3D"https://www.iso.org/standard/80985.html">https://www=
.iso.org/standard/80985.html</a>=C2=A0 (which mentioned in=C2=A0<a href=3D"=
https://en.cppreference.com/c/numeric/math/fmax">https://en.cppreference.co=
m/c/numeric/math/fmax</a>=C2=A0 =C2=A0 for example) which behavior is more =
likely &quot;option 1&quot;=C2=A0</div><br><div class=3D"gmail_quote gmail_=
quote_container"><div dir=3D"ltr" class=3D"gmail_attr">=D0=B2=D1=81, 2 =D0=
=B0=D0=B2=D0=B3. 2026=E2=80=AF=D0=B3. =D0=B2 01:56, Peter McGoron via Chick=
en-users &lt;<a href=3D"mailto:[email protected]">chicken-users@nong=
nu.org</a>&gt;:<br></div><blockquote class=3D"gmail_quote" style=3D"margin:=
0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">=
This is on the latest master.<br>
<br>
=C2=A0 =C2=A0 =C2=A0 (max +nan.0 1.0 2.0) =3D&gt; +nan.0<br>
=C2=A0 =C2=A0 =C2=A0 (max 1.0 +nan.0 2.0) =3D&gt; 2.0<br>
=C2=A0 =C2=A0 =C2=A0 (max 1.0 2.0 +nan.0) =3D&gt; 2.0<br>
<br>
This looks like these procedures are using `&lt;` and `&gt;` to compare <br=
>
numbers, which fails on NaN.<br>
<br>
There are two IEEE ways to handle NaNs in max and min:<br>
<br>
1. Ignore them when possible, and only return a NaN when all arguments <br>
are NaNs. So the above examples would be equivalent to `(max 1.0 2.0)`.<br>
<br>
2. If any input arguments are NaN, then return a NaN. So each example <br>
above would return NaN.<br>
<br>
The R7RS doesn&#39;t specify any of these behaviors.<br>
<br>
On the implementations I&#39;ve tested the examples on that didn&#39;t have=
 <br>
inconsistent results, they all returned NaN when any input is a NaN. So <br=
>
I recommend the second option because it will cause more portable <br>
behavior across implementations.<br>
<br>
My suggestion is to replace `(if (&gt; h m) h m)` in the definition of <br>
`max` with `(##max2 h m)`, where for the two options above:<br>
<br>
(define (##max2-option1 h m)<br>
=C2=A0 =C2=A0(cond<br>
=C2=A0 =C2=A0 =C2=A0((and (nan? h) (nan? m)) h)<br>
=C2=A0 =C2=A0 =C2=A0((nan? h) m)<br>
=C2=A0 =C2=A0 =C2=A0((nan? m) m)<br>
=C2=A0 =C2=A0 =C2=A0((and (eqv? h -0.0) (eqv? m +0.0)) m)<br>
=C2=A0 =C2=A0 =C2=A0((and (eqv? h +0.0) (eqv? m -0.0)) h)<br>
=C2=A0 =C2=A0 =C2=A0((&lt; h m) m)<br>
=C2=A0 =C2=A0 =C2=A0(else h)))<br>
<br>
(define (##max2-option2 h m)<br>
=C2=A0 =C2=A0(cond<br>
=C2=A0 =C2=A0 =C2=A0((nan? h) h)<br>
=C2=A0 =C2=A0 =C2=A0((nan? m) m)<br>
=C2=A0 =C2=A0 =C2=A0((and (eqv? h -0.0) (eqv? m +0.0)) m)<br>
=C2=A0 =C2=A0 =C2=A0((and (eqv? h +0.0) (eqv? m -0.0)) h)<br>
=C2=A0 =C2=A0 =C2=A0((&lt; h m) m)<br>
=C2=A0 =C2=A0 =C2=A0(else h)))<br>
<br>
IEEE 754-2019 mandates that, for the purposes of max and min, +0.0 is <br>
greater than -0.0. Similar things apply to min.<br>
<br>
I would add the following test cases:<br>
<br>
=C2=A0 =C2=A0 =C2=A0 (max +nan.0 1.0 2.0) =3D&gt; 2.0 (option 1) OR +nan.0 =
(option 2)<br>
=C2=A0 =C2=A0 =C2=A0 (max 1.0 +nan.0 2.0) =3D&gt; same<br>
=C2=A0 =C2=A0 =C2=A0 (max 1.0 2.0 +nan.0) =3D&gt; same<br>
=C2=A0 =C2=A0 =C2=A0 (max +nan.0)=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=3D&gt; =
+nan.0<br>
=C2=A0 =C2=A0 =C2=A0 (max +nan.0 +nan.0)=C2=A0 =3D&gt; +nan.0<br>
=C2=A0 =C2=A0 =C2=A0 (max -0.0 +0.0)=C2=A0 =C2=A0 =C2=A0 =3D&gt; +0.0<br>
=C2=A0 =C2=A0 =C2=A0 (max +0.0 -0.0)=C2=A0 =C2=A0 =C2=A0 =3D&gt; +0.0<br>
<br>
=C2=A0 =C2=A0 =C2=A0 (min +nan.0 1.0 2.0) =3D&gt; 1.0 (option 1) OR +nan.0 =
(option 2)<br>
=C2=A0 =C2=A0 =C2=A0 (min 1.0 +nan.0 2.0) =3D&gt; same<br>
=C2=A0 =C2=A0 =C2=A0 (min 1.0 2.0 +nan.0) =3D&gt; same<br>
=C2=A0 =C2=A0 =C2=A0 (min +nan.0)=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=3D&gt; =
+nan.0<br>
=C2=A0 =C2=A0 =C2=A0 (min +nan.0 +nan.0)=C2=A0 =3D&gt; +nan.0<br>
=C2=A0 =C2=A0 =C2=A0 (min -0.0 +0.0)=C2=A0 =C2=A0 =C2=A0 =3D&gt; -0.0<br>
=C2=A0 =C2=A0 =C2=A0 (min +0.0 -0.0)=C2=A0 =C2=A0 =C2=A0 =3D&gt; -0.0<br>
<br>
-- Peter McGoron<br>
<br>
</blockquote></div>

--000000000000775e68065808e502--