> Am 17.06.2025 um 23:04 schrieb Jakub Jelinek <ja...@redhat.com>:
>
> Hi!
>
> The following testcase shows that while at runtime we handle conversions
> between _Decimal{64,128} and large _BitInt correctly, at compile time we
> mishandle them in both directions, in one direction we end up in ICE in
> decimal_from_integer callee because the char buffer is too short for the
> needed number of decimal digits, in the conversion of dfp to large _BitInt
> we return 0 in the wide_int.
>
> The following patch fixes the ICE by using larger buffer (XALLOCAVEC
> allocated, it will be never larger than 65536 / 3 bytes) in the larger
> _BitInt case, and the other direction by setting exponent to exp % 19
> and instead multiplying the result by needed powers of 10^19 (10^19 chosen
> as largest power of ten that can fit into UHWI).
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for
> trunk/15.2/14.4?
Ok
Richard
> 2025-06-17 Jakub Jelinek <ja...@redhat.com>
>
> PR middle-end/120631
> * real.cc (decimal_from_integer): Add digits argument, if larger than
> 256, use XALLOCAVEC allocated buffer.
> (real_from_integer): Pass val_in's precision divided by 3 to
> decimal_from_integer.
> * dfp.cc (decimal_real_to_integer): For precision > 128 if finite
> and exponent is large, decrease exponent and multiply resulting
> wide_int by powers of 10^19.
>
> * gcc.dg/dfp/bitint-9.c: New test.
>
> --- gcc/real.cc.jj 2025-06-05 15:47:08.313244971 +0200
> +++ gcc/real.cc 2025-06-17 14:40:16.565601561 +0200
> @@ -101,7 +101,7 @@ static int do_compare (const REAL_VALUE_
> static void do_fix_trunc (REAL_VALUE_TYPE *, const REAL_VALUE_TYPE *);
>
> static unsigned long rtd_divmod (REAL_VALUE_TYPE *, REAL_VALUE_TYPE *);
> -static void decimal_from_integer (REAL_VALUE_TYPE *);
> +static void decimal_from_integer (REAL_VALUE_TYPE *, int);
> static void decimal_integer_string (char *, const REAL_VALUE_TYPE *,
> size_t);
>
> @@ -2309,7 +2309,9 @@ real_from_integer (REAL_VALUE_TYPE *r, f
> }
>
> if (fmt.decimal_p ())
> - decimal_from_integer (r);
> + /* We need at most one decimal digits for each 3 bits of input
> + precision. */
> + decimal_from_integer (r, val_in.get_precision () / 3);
> if (fmt)
> real_convert (r, fmt, r);
> }
> @@ -2364,12 +2366,21 @@ decimal_integer_string (char *str, const
> /* Convert a real with an integral value to decimal float. */
>
> static void
> -decimal_from_integer (REAL_VALUE_TYPE *r)
> +decimal_from_integer (REAL_VALUE_TYPE *r, int digits)
> {
> char str[256];
>
> - decimal_integer_string (str, r, sizeof (str) - 1);
> - decimal_real_from_string (r, str);
> + if (digits <= 256)
> + {
> + decimal_integer_string (str, r, sizeof (str) - 1);
> + decimal_real_from_string (r, str);
> + }
> + else
> + {
> + char *s = XALLOCAVEC (char, digits);
> + decimal_integer_string (s, r, digits - 1);
> + decimal_real_from_string (r, s);
> + }
> }
>
> /* Returns 10**2**N. */
> --- gcc/dfp.cc.jj 2025-04-08 14:08:50.669289967 +0200
> +++ gcc/dfp.cc 2025-06-17 17:28:25.101792744 +0200
> @@ -619,11 +619,21 @@ decimal_real_to_integer (const REAL_VALU
> decNumber dn, dn2, dn3;
> REAL_VALUE_TYPE to;
> char string[256];
> + int scale = 0;
>
> decContextDefault (&set, DEC_INIT_DECIMAL128);
> set.traps = 0;
> set.round = DEC_ROUND_DOWN;
> decimal128ToNumber ((const decimal128 *) r->sig, &dn);
> + if (precision > 128 && decNumberIsFinite (&dn) && dn.exponent > 19)
> + {
> + /* libdecNumber doesn't really handle too large integers.
> + So when precision is large and exponent as well, trim the
> + exponent and adjust the resulting wide_int by multiplying
> + it multiple times with 10^19. */
> + scale = dn.exponent / 19;
> + dn.exponent %= 19;
> + }
>
> decNumberToIntegralValue (&dn2, &dn, &set);
> decNumberZero (&dn3);
> @@ -633,7 +643,44 @@ decimal_real_to_integer (const REAL_VALU
> function. */
> decNumberToString (&dn, string);
> real_from_string (&to, string);
> - return real_to_integer (&to, fail, precision);
> + bool failp = false;
> + wide_int w = real_to_integer (&to, &failp, precision);
> + if (failp)
> + *fail = true;
> + if (scale && !failp)
> + {
> + wide_int wm = wi::uhwi (HOST_WIDE_INT_UC (10000000000000000000),
> + w.get_precision ());
> + bool isneg = wi::neg_p (w);
> + if (isneg)
> + w = -w;
> + enum wi::overflow_type ovf = wi::OVF_NONE;
> + do
> + {
> + if (scale & 1)
> + {
> + w = wi::umul (w, wm, &ovf);
> + if (ovf)
> + break;
> + }
> + scale >>= 1;
> + if (!scale)
> + break;
> + wm = wi::umul (wm, wm, &ovf);
> + }
> + while (!ovf);
> + if (ovf)
> + {
> + *fail = true;
> + if (isneg)
> + return wi::set_bit_in_zero (precision - 1, precision);
> + else
> + return ~wi::set_bit_in_zero (precision - 1, precision);
> + }
> + if (isneg)
> + w = -w;
> + }
> + return w;
> }
>
> /* Perform the decimal floating point operation described by CODE.
> --- gcc/testsuite/gcc.dg/dfp/bitint-9.c.jj 2025-06-17 17:30:13.271374809
> +0200
> +++ gcc/testsuite/gcc.dg/dfp/bitint-9.c 2025-06-17 14:53:36.997994620 +0200
> @@ -0,0 +1,29 @@
> +/* PR middle-end/120631 */
> +/* { dg-do run { target bitint } } */
> +/* { dg-options "-O2" } */
> +
> +#if __BITINT_MAXWIDTH__ >= 2048
> +_Decimal128 a =
> 123456789135792468012345678900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0dl;
> +_BitInt(2048) b =
> 123456789135792468012345678900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000wb;
> +_Decimal64 c =
> 123456789135790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0dd;
> +_BitInt(1536) d =
> 123456789135790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000wb;
> +#endif
> +
> +int
> +main ()
> +{
> +#if __BITINT_MAXWIDTH__ >= 2048
> + if (a != b || (_BitInt(2048)) a != b || c != d || (_BitInt(1536)) c != d)
> + __builtin_abort ();
> + _Decimal128 e =
> 123456789135792468012345678900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0dl;
> + _BitInt(2048) f =
> 123456789135792468012345678900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000wb;
> + _Decimal128 g =
> 123456789135792468012345678900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000wb;
> + _BitInt(2048) h =
> 123456789135792468012345678900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0dl;
> + _Decimal64 i =
> 123456789135790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0dd;
> + _BitInt(1536) j =
> 123456789135790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000wb;
> + _Decimal64 k =
> 123456789135790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000wb;
> + _BitInt(1536) l =
> 123456789135790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0dd;
> + if (e != g || f != h || i != k || j != l)
> + __builtin_abort ();
> +#endif
> +}
>
> Jakub
>