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?

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

Reply via email to