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 "option 1"=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 <<a href=3D"mailto:[email protected]">chicken-users@nong= nu.org</a>>:<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> +nan.0<br> =C2=A0 =C2=A0 =C2=A0 (max 1.0 +nan.0 2.0) =3D> 2.0<br> =C2=A0 =C2=A0 =C2=A0 (max 1.0 2.0 +nan.0) =3D> 2.0<br> <br> This looks like these procedures are using `<` and `>` 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't specify any of these behaviors.<br> <br> On the implementations I've tested the examples on that didn'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 (> 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((< 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((< 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> 2.0 (option 1) OR +nan.0 = (option 2)<br> =C2=A0 =C2=A0 =C2=A0 (max 1.0 +nan.0 2.0) =3D> same<br> =C2=A0 =C2=A0 =C2=A0 (max 1.0 2.0 +nan.0) =3D> same<br> =C2=A0 =C2=A0 =C2=A0 (max +nan.0)=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=3D> = +nan.0<br> =C2=A0 =C2=A0 =C2=A0 (max +nan.0 +nan.0)=C2=A0 =3D> +nan.0<br> =C2=A0 =C2=A0 =C2=A0 (max -0.0 +0.0)=C2=A0 =C2=A0 =C2=A0 =3D> +0.0<br> =C2=A0 =C2=A0 =C2=A0 (max +0.0 -0.0)=C2=A0 =C2=A0 =C2=A0 =3D> +0.0<br> <br> =C2=A0 =C2=A0 =C2=A0 (min +nan.0 1.0 2.0) =3D> 1.0 (option 1) OR +nan.0 = (option 2)<br> =C2=A0 =C2=A0 =C2=A0 (min 1.0 +nan.0 2.0) =3D> same<br> =C2=A0 =C2=A0 =C2=A0 (min 1.0 2.0 +nan.0) =3D> same<br> =C2=A0 =C2=A0 =C2=A0 (min +nan.0)=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=3D> = +nan.0<br> =C2=A0 =C2=A0 =C2=A0 (min +nan.0 +nan.0)=C2=A0 =3D> +nan.0<br> =C2=A0 =C2=A0 =C2=A0 (min -0.0 +0.0)=C2=A0 =C2=A0 =C2=A0 =3D> -0.0<br> =C2=A0 =C2=A0 =C2=A0 (min +0.0 -0.0)=C2=A0 =C2=A0 =C2=A0 =3D> -0.0<br> <br> -- Peter McGoron<br> <br> </blockquote></div> --000000000000775e68065808e502--