diff options
Diffstat (limited to 'results/classifier/105/graphic/2290')
| -rw-r--r-- | results/classifier/105/graphic/2290 | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/results/classifier/105/graphic/2290 b/results/classifier/105/graphic/2290 new file mode 100644 index 000000000..d5cdb77df --- /dev/null +++ b/results/classifier/105/graphic/2290 @@ -0,0 +1,156 @@ +graphic: 0.950 +other: 0.942 +semantic: 0.940 +assembly: 0.932 +instruction: 0.927 +device: 0.919 +KVM: 0.893 +boot: 0.887 +socket: 0.862 +mistranslation: 0.853 +vnc: 0.853 +network: 0.821 + +Wrong multiplication result of 'long double' on m68k +Description of problem: +In both x86 and m68k, 'long double' is an 80-bit format consisting of + - 1 bit sign, 15 bits exponent, + - 1 explicit 1 bit, 63 fraction bits. + +According to <https://en.wikipedia.org/wiki/Extended_precision> and +<https://www.nxp.com/docs/en/reference-manual/M68000PRM.pdf> table 1-6 (page 1-23), with two differences: + - In m68k, there are 16 zero bits as filler after the sign/exponent + word, so that the total size is 96 bits. + - In x86, the minimum exponent of normalized numbers is 1; + in m68k, the minimum exponent of normalized numbers is 0. + +The latter difference is reflected in the values of LDBL_MIN_EXP and +LDBL_MIN in gcc: + +In x86: +``` +$ echo '#include <float.h>' | gcc -E -dM - | grep __LDBL_MIN_EXP_ +#define LDBL_MIN_EXP __LDBL_MIN_EXP__ +#define __LDBL_MIN_EXP__ (-16381) +$ echo '#include <float.h>' | gcc -E -dM - | grep __LDBL_MIN__ +#define __LDBL_MIN__ 3.36210314311209350626267781732175260e-4932L +#define LDBL_MIN __LDBL_MIN__ +``` +In m68k (I use Debian 12/Linux): +``` +$ echo '#include <float.h>' | gcc -E -dM - | grep __LDBL_MIN_EXP_ +#define LDBL_MIN_EXP __LDBL_MIN_EXP__ +#define __LDBL_MIN_EXP__ (-16382) +$ echo '#include <float.h>' | gcc -E -dM - | grep __LDBL_MIN__ +#define __LDBL_MIN__ 1.68105157155604675313e-4932L +#define LDBL_MIN __LDBL_MIN__ +``` +Steps to reproduce: +Take this program, foo.c: +``` +/* Show extended-precision https://en.wikipedia.org/wiki/Extended_precision + multiplication bug in QEMU. */ + +#include <stdio.h> + +static void +show (const long double *p) +{ +#ifdef __m68k__ + printf("<S,E: 0x%08X M: 0x%08X%08X>", + ((const unsigned int *) p)[0], + ((const unsigned int *) p)[1], + ((const unsigned int *) p)[2]); +#else /* x86 */ + printf("<S,E: 0x%04X M: 0x%08X%08X>", + ((const unsigned short *) p)[4], + ((const unsigned int *) p)[1], + ((const unsigned int *) p)[0]); +#endif + printf (" = %La = %Lg", *p, *p); +} + +static void +show_mult (long double a, long double b) +{ + printf ("Factors: "); + show (&a); + printf ("\n and: "); + show (&b); + long double c = a * b; + printf ("\nProduct: "); + show (&c); + printf ("\n\n"); +} + +/* Return 2^n. */ +static long double +pow2l (int n) +{ + int k = n; + volatile long double x = 1; + volatile long double y = 2; + /* Invariant: 2^n == x * y^k. */ + if (k < 0) + { + y = 0.5L; + k = - k; + } + while (k > 0) + { + if (k != 2 * (k / 2)) + { + x = x * y; + k = k - 1; + } + if (k == 0) + break; + y = y * y; + k = k / 2; + } + /* Now k == 0, hence x == 2^n. */ + return x; +} + +int main () +{ + show_mult (pow2l (-16382), 0.5L); + show_mult (pow2l (-16381), 0.25L); + return 0; +} +``` +Its output on x86: +``` +$ ./a.out +Factors: <S,E: 0x0001 M: 0x8000000000000000> = 0x8p-16385 = 3.3621e-4932 + and: <S,E: 0x3FFE M: 0x8000000000000000> = 0x8p-4 = 0.5 +Product: <S,E: 0x0000 M: 0x4000000000000000> = 0x4p-16385 = 1.68105e-4932 + +Factors: <S,E: 0x0002 M: 0x8000000000000000> = 0x8p-16384 = 6.72421e-4932 + and: <S,E: 0x3FFD M: 0x8000000000000000> = 0x8p-5 = 0.25 +Product: <S,E: 0x0000 M: 0x4000000000000000> = 0x4p-16385 = 1.68105e-4932 +``` +Its output on m68k: +``` +$ ./a.out +Factors: <S,E: 0x00010000 M: 0x8000000000000000> = 0x8p-16385 = 3.3621e-4932 + and: <S,E: 0x3FFE0000 M: 0x8000000000000000> = 0x8p-4 = 0.5 +Product: <S,E: 0x00000000 M: 0x4000000000000000> = 0x4p-16386 = 8.40526e-4933 + +Factors: <S,E: 0x00020000 M: 0x8000000000000000> = 0x8p-16384 = 6.72421e-4932 + and: <S,E: 0x3FFD0000 M: 0x8000000000000000> = 0x8p-5 = 0.25 +Product: <S,E: 0x00000000 M: 0x4000000000000000> = 0x4p-16386 = 8.40526e-4933 +``` +The product, computed by QEMU, is incorrect. It is only half as large as the +correct value. The expected output should be: +``` +Factors: <S,E: 0x00010000 M: 0x8000000000000000> = 0x8p-16385 = 3.3621e-4932 + and: <S,E: 0x3FFE0000 M: 0x8000000000000000> = 0x8p-4 = 0.5 +Product: <S,E: 0x00000000 M: 0x8000000000000000> = 0x8p-16386 = 1.68105e-4932 + +Factors: <S,E: 0x00020000 M: 0x8000000000000000> = 0x8p-16384 = 6.72421e-4932 + and: <S,E: 0x3FFD0000 M: 0x8000000000000000> = 0x8p-5 = 0.25 +Product: <S,E: 0x00000000 M: 0x8000000000000000> = 0x8p-16386 = 1.68105e-4932 +``` +Additional information: +In QEMU's source code, I would guess that this multiplication is performed by the `floatx80_mul` function. |