From ludovic at rivosinc.com Thu Dec 8 15:19:02 2022 From: ludovic at rivosinc.com (Ludovic Henry) Date: Thu, 8 Dec 2022 12:19:02 -0300 Subject: Difference in behaviour in native math library Message-ID: Hello, I've noticed that some Math trigonometry tests are failing in the GNU Mauve test suite. From digging into it, it's related to NaN values being passed to java.lang.Math trigonometry functions, and how these values are handled in the native libm library. Given the following C test case compiled and run with `gcc acos.c -lm && ./a.out` ``` #include #include #include #include void main(int argc, char* argv[]) { int64_t bitsNaN = 0x7fff800000000000L; double valNaN = *((double*)&bitsNaN); double resD = acos(valNaN); int64_t res = *((int64_t*)&resD); if (!(res == bitsNaN)) { printf("expected 0x%lx but got 0x%lx\n", bitsNaN, res); exit(1); } } ``` On a Linux-x64, the test succeeds, but on Linux-RISC-V, the test fails. You've the same test failure in the equivalent Java code: ``` public class acos { public static void main (String[] args) { long bitsNaN = 0x7fff800000000000L; double valNaN = Double.longBitsToDouble(bitsNaN); long res = Double.doubleToRawLongBits(Math.acos(valNaN)); if (!(res == bitsNaN)) { throw new RuntimeException(String.format("expected 0x%x but got 0x%x", bitsNaN, res)); } } } ``` What approach should we take in these cases? Is it that the test case is wrong, and should assume that given a NaN, any value of NaN returned is valid? Or should we make sure that the behavior is the same across platforms and that we "fix" any difference in behavior of the native library? Cheers, Ludovic -------------- next part -------------- An HTML attachment was scrubbed... URL: From ludovic at rivosinc.com Thu Dec 8 16:26:30 2022 From: ludovic at rivosinc.com (Ludovic Henry) Date: Thu, 8 Dec 2022 13:26:30 -0300 Subject: Difference in behaviour in native math library In-Reply-To: References: Message-ID: Adding the right address for core-libs-dev. On Thu, Dec 8, 2022 at 12:19 PM Ludovic Henry wrote: > Hello, > > I've noticed that some Math trigonometry tests are failing in the GNU > Mauve test suite. From digging into it, it's related to NaN values being > passed to java.lang.Math trigonometry functions, and how these values are > handled in the native libm library. > > Given the following C test case compiled and run with `gcc acos.c -lm && > ./a.out` > > ``` > #include > #include > #include > #include > > void main(int argc, char* argv[]) { > int64_t bitsNaN = 0x7fff800000000000L; > double valNaN = *((double*)&bitsNaN); > > double resD = acos(valNaN); > int64_t res = *((int64_t*)&resD); > if (!(res == bitsNaN)) { > printf("expected 0x%lx but got 0x%lx\n", bitsNaN, res); > exit(1); > } > } > ``` > > On a Linux-x64, the test succeeds, but on Linux-RISC-V, the test fails. > > You've the same test failure in the equivalent Java code: > > ``` > public class acos { > public static void main (String[] args) { > long bitsNaN = 0x7fff800000000000L; > double valNaN = Double.longBitsToDouble(bitsNaN); > > long res = Double.doubleToRawLongBits(Math.acos(valNaN)); > if (!(res == bitsNaN)) { > throw new RuntimeException(String.format("expected 0x%x but > got 0x%x", bitsNaN, res)); > } > } > } > ``` > > What approach should we take in these cases? Is it that the test case is > wrong, and should assume that given a NaN, any value of NaN returned is > valid? Or should we make sure that the behavior is the same across > platforms and that we "fix" any difference in behavior of the native > library? > > Cheers, > Ludovic > -------------- next part -------------- An HTML attachment was scrubbed... URL: From palmer at dabbelt.com Thu Dec 8 17:50:54 2022 From: palmer at dabbelt.com (Palmer Dabbelt) Date: Thu, 08 Dec 2022 09:50:54 -0800 (PST) Subject: Difference in behaviour in native math library In-Reply-To: Message-ID: On Thu, 08 Dec 2022 08:26:30 PST (-0800), ludovic at rivosinc.com wrote: > Adding the right address for core-libs-dev. > > On Thu, Dec 8, 2022 at 12:19 PM Ludovic Henry wrote: > >> Hello, >> >> I've noticed that some Math trigonometry tests are failing in the GNU >> Mauve test suite. From digging into it, it's related to NaN values being >> passed to java.lang.Math trigonometry functions, and how these values are >> handled in the native libm library. >> >> Given the following C test case compiled and run with `gcc acos.c -lm && >> ./a.out` >> >> ``` >> #include >> #include >> #include >> #include >> >> void main(int argc, char* argv[]) { >> int64_t bitsNaN = 0x7fff800000000000L; >> double valNaN = *((double*)&bitsNaN); >> >> double resD = acos(valNaN); >> int64_t res = *((int64_t*)&resD); >> if (!(res == bitsNaN)) { >> printf("expected 0x%lx but got 0x%lx\n", bitsNaN, res); >> exit(1); >> } >> } >> ``` >> >> On a Linux-x64, the test succeeds, but on Linux-RISC-V, the test fails. >> >> You've the same test failure in the equivalent Java code: >> >> ``` >> public class acos { >> public static void main (String[] args) { >> long bitsNaN = 0x7fff800000000000L; >> double valNaN = Double.longBitsToDouble(bitsNaN); >> >> long res = Double.doubleToRawLongBits(Math.acos(valNaN)); >> if (!(res == bitsNaN)) { >> throw new RuntimeException(String.format("expected 0x%x but >> got 0x%x", bitsNaN, res)); >> } >> } >> } >> ``` >> >> What approach should we take in these cases? Is it that the test case is >> wrong, and should assume that given a NaN, any value of NaN returned is >> valid? Or should we make sure that the behavior is the same across >> platforms and that we "fix" any difference in behavior of the native >> library? It might just be a glibc bug, we're failing the acos() tests: https://sourceware.org/glibc/wiki/Release/2.35#RISC-V_.28rv64imafdc.2Flp64d.29 . I haven't looked at why, but they do have some nan-related bits in there. >> >> Cheers, >> Ludovic >> From ludovic at rivosinc.com Thu Dec 8 18:01:23 2022 From: ludovic at rivosinc.com (Ludovic Henry) Date: Thu, 8 Dec 2022 15:01:23 -0300 Subject: Difference in behaviour in native math library In-Reply-To: References: Message-ID: Hi, Yes, this is very much a difference of behavior at the libm/libc level. I'm trying to figure out whether that behavior difference is acceptable from the libm/libc level (is it considered a bug to behave differently across architectures in that case?). If that behaviour is acceptable in libm/libc, then is it acceptable to behave differently in Java across architectures? >From your answer Palmer, the difference _might_ not be acceptable in the glibc test suite (I haven't check why the tests are failing). I'm still trying to figure that one out. >From your answer Joe, the difference _is_ acceptable from the documentation. IMO the question becomes whether it's a case of the implementation details bleeding into the API and whether we want to be 100% compatible to avoid further issues. The fix for that could be a simple `RISCV_ONLY(if (isnan(v)) return v);` or equivalent in src/java.base/share/native/libjava/StrictMath.c to match the behavior without any extra cost for other platforms. Thanks, Ludovic On Thu, Dec 8, 2022 at 2:50 PM Palmer Dabbelt wrote: > On Thu, 08 Dec 2022 08:26:30 PST (-0800), ludovic at rivosinc.com wrote: > > Adding the right address for core-libs-dev. > > > > On Thu, Dec 8, 2022 at 12:19 PM Ludovic Henry > wrote: > > > >> Hello, > >> > >> I've noticed that some Math trigonometry tests are failing in the GNU > >> Mauve test suite. From digging into it, it's related to NaN values being > >> passed to java.lang.Math trigonometry functions, and how these values > are > >> handled in the native libm library. > >> > >> Given the following C test case compiled and run with `gcc acos.c -lm && > >> ./a.out` > >> > >> ``` > >> #include > >> #include > >> #include > >> #include > >> > >> void main(int argc, char* argv[]) { > >> int64_t bitsNaN = 0x7fff800000000000L; > >> double valNaN = *((double*)&bitsNaN); > >> > >> double resD = acos(valNaN); > >> int64_t res = *((int64_t*)&resD); > >> if (!(res == bitsNaN)) { > >> printf("expected 0x%lx but got 0x%lx\n", bitsNaN, res); > >> exit(1); > >> } > >> } > >> ``` > >> > >> On a Linux-x64, the test succeeds, but on Linux-RISC-V, the test fails. > >> > >> You've the same test failure in the equivalent Java code: > >> > >> ``` > >> public class acos { > >> public static void main (String[] args) { > >> long bitsNaN = 0x7fff800000000000L; > >> double valNaN = Double.longBitsToDouble(bitsNaN); > >> > >> long res = Double.doubleToRawLongBits(Math.acos(valNaN)); > >> if (!(res == bitsNaN)) { > >> throw new RuntimeException(String.format("expected 0x%x but > >> got 0x%x", bitsNaN, res)); > >> } > >> } > >> } > >> ``` > >> > >> What approach should we take in these cases? Is it that the test case is > >> wrong, and should assume that given a NaN, any value of NaN returned is > >> valid? Or should we make sure that the behavior is the same across > >> platforms and that we "fix" any difference in behavior of the native > >> library? > > It might just be a glibc bug, we're failing the acos() tests: > > https://sourceware.org/glibc/wiki/Release/2.35#RISC-V_.28rv64imafdc.2Flp64d.29 > . I haven't looked at why, but they do have some nan-related bits in > there. > > >> > >> Cheers, > >> Ludovic > >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ludovic at rivosinc.com Thu Dec 8 18:25:51 2022 From: ludovic at rivosinc.com (Ludovic Henry) Date: Thu, 8 Dec 2022 15:25:51 -0300 Subject: Difference in behaviour in native math library In-Reply-To: References: Message-ID: I've submitted the question to the glibc mailing list at https://sourceware.org/pipermail/libc-alpha/2022-December/143909.html, I'll keep you posted on any news. On Thu, Dec 8, 2022 at 3:01 PM Ludovic Henry wrote: > Hi, > > Yes, this is very much a difference of behavior at the libm/libc level. > I'm trying to figure out whether that behavior difference is acceptable > from the libm/libc level (is it considered a bug to behave differently > across architectures in that case?). If that behaviour is acceptable in > libm/libc, then is it acceptable to behave differently in Java across > architectures? > > From your answer Palmer, the difference _might_ not be acceptable in the > glibc test suite (I haven't check why the tests are failing). I'm still > trying to figure that one out. > > From your answer Joe, the difference _is_ acceptable from the > documentation. IMO the question becomes whether it's a case of the > implementation details bleeding into the API and whether we want to be 100% > compatible to avoid further issues. The fix for that could be a simple > `RISCV_ONLY(if (isnan(v)) return v);` or equivalent > in src/java.base/share/native/libjava/StrictMath.c to match the behavior > without any extra cost for other platforms. > > Thanks, > Ludovic > > On Thu, Dec 8, 2022 at 2:50 PM Palmer Dabbelt wrote: > >> On Thu, 08 Dec 2022 08:26:30 PST (-0800), ludovic at rivosinc.com wrote: >> > Adding the right address for core-libs-dev. >> > >> > On Thu, Dec 8, 2022 at 12:19 PM Ludovic Henry >> wrote: >> > >> >> Hello, >> >> >> >> I've noticed that some Math trigonometry tests are failing in the GNU >> >> Mauve test suite. From digging into it, it's related to NaN values >> being >> >> passed to java.lang.Math trigonometry functions, and how these values >> are >> >> handled in the native libm library. >> >> >> >> Given the following C test case compiled and run with `gcc acos.c -lm >> && >> >> ./a.out` >> >> >> >> ``` >> >> #include >> >> #include >> >> #include >> >> #include >> >> >> >> void main(int argc, char* argv[]) { >> >> int64_t bitsNaN = 0x7fff800000000000L; >> >> double valNaN = *((double*)&bitsNaN); >> >> >> >> double resD = acos(valNaN); >> >> int64_t res = *((int64_t*)&resD); >> >> if (!(res == bitsNaN)) { >> >> printf("expected 0x%lx but got 0x%lx\n", bitsNaN, res); >> >> exit(1); >> >> } >> >> } >> >> ``` >> >> >> >> On a Linux-x64, the test succeeds, but on Linux-RISC-V, the test fails. >> >> >> >> You've the same test failure in the equivalent Java code: >> >> >> >> ``` >> >> public class acos { >> >> public static void main (String[] args) { >> >> long bitsNaN = 0x7fff800000000000L; >> >> double valNaN = Double.longBitsToDouble(bitsNaN); >> >> >> >> long res = Double.doubleToRawLongBits(Math.acos(valNaN)); >> >> if (!(res == bitsNaN)) { >> >> throw new RuntimeException(String.format("expected 0x%x but >> >> got 0x%x", bitsNaN, res)); >> >> } >> >> } >> >> } >> >> ``` >> >> >> >> What approach should we take in these cases? Is it that the test case >> is >> >> wrong, and should assume that given a NaN, any value of NaN returned is >> >> valid? Or should we make sure that the behavior is the same across >> >> platforms and that we "fix" any difference in behavior of the native >> >> library? >> >> It might just be a glibc bug, we're failing the acos() tests: >> >> https://sourceware.org/glibc/wiki/Release/2.35#RISC-V_.28rv64imafdc.2Flp64d.29 >> . I haven't looked at why, but they do have some nan-related bits in >> there. >> >> >> >> >> Cheers, >> >> Ludovic >> >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From joe.darcy at oracle.com Thu Dec 8 17:31:07 2022 From: joe.darcy at oracle.com (Joseph D. Darcy) Date: Thu, 8 Dec 2022 09:31:07 -0800 Subject: Difference in behaviour in native math library In-Reply-To: References: Message-ID: <6bc2bc45-a730-e445-51a4-53ae6ecb653e@oracle.com> Hello, Okay, so it looks like the test is expected the same bit pattern to be used for a NaN output if a NaN was used as an input. That isn't necessarily unreasonable, but it is *not* required by the specifications for the Math or StrictMath method, spec for Math.acos: > Returns the arc cosine of a value; the returned angle is in the range > 0.0 through /pi/. Special case: > > * If the argument is NaN or its absolute value is greater than 1, > then the result is NaN. > * If the argument is |1.0|, the result is positive zero. > > The computed result must be within 1 ulp of the exact result. Results > must be semi-monotonic. > https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/Math.html#acos(double) On a NaN argument, any NaN bit pattern is valid per the spec. Even if you're trying to return the same NaN, there can be challenges to doing so depending on how the underlying processor handles signaling NaNs, a concept which doesn't exist in the Java platform. HTH, -Joe On 12/8/2022 8:26 AM, Ludovic Henry wrote: > Adding the right address for core-libs-dev. > > On Thu, Dec 8, 2022 at 12:19 PM Ludovic Henry > wrote: > > Hello, > > I've noticed that some Math trigonometry tests are failing in the > GNU Mauve test suite. From digging into it, it's related to NaN > values being passed to java.lang.Math trigonometry functions, and > how these values are handled in the native libm library. > > Given the following C test case compiled and run with `gcc acos.c > -lm && ./a.out` > > ``` > #include > #include > #include > #include > > void main(int argc, char* argv[]) { > ? ? int64_t bitsNaN = 0x7fff800000000000L; > ? ? double valNaN = *((double*)&bitsNaN); > > ? ? double resD = acos(valNaN); > ? ? int64_t res = *((int64_t*)&resD); > ? ? if (!(res == bitsNaN)) { > ? ? ? ? printf("expected 0x%lx but got 0x%lx\n", bitsNaN, res); > ? ? ? ? exit(1); > ? ? } > } > ``` > > On a Linux-x64, the test succeeds, but on Linux-RISC-V, the test > fails. > > You've the same test failure in the equivalent Java code: > > ``` > public class acos { > ? ? public static void main (String[] args) { > ? ? ? ? long bitsNaN = 0x7fff800000000000L; > ? ? ? ? double valNaN = Double.longBitsToDouble(bitsNaN); > > ? ? ? ? long res = Double.doubleToRawLongBits(Math.acos(valNaN)); > ? ? ? ? if (!(res == bitsNaN)) { > ? ? ? ? ? ? throw new RuntimeException(String.format("expected > 0x%x but got 0x%x", bitsNaN, res)); > ? ? ? ? } > ? ? } > } > ``` > > What approach should we take in these cases? Is it that the test > case is wrong, and should assume that given a NaN, any value of > NaN returned is valid? Or should we make sure that the behavior is > the same across platforms and that we "fix" any difference in > behavior of the native library? > > Cheers, > Ludovic > -------------- next part -------------- An HTML attachment was scrubbed... URL: