summary refs log tree commit diff stats
path: root/libdecnumber/decNumber.c
diff options
context:
space:
mode:
authorLuis Pires <luis.pires@eldorado.org.br>2021-10-29 16:24:08 -0300
committerDavid Gibson <david@gibson.dropbear.id.au>2021-11-09 10:32:52 +1100
commit21d7826fdbf13bc3180f8f23f3f87967604fdf7e (patch)
treed5d3772fefece921e03557d2866506af83cd4a3d /libdecnumber/decNumber.c
parente06049f38074b9533c4beef37d634dad3bd97c73 (diff)
downloadfocaccia-qemu-21d7826fdbf13bc3180f8f23f3f87967604fdf7e.tar.gz
focaccia-qemu-21d7826fdbf13bc3180f8f23f3f87967604fdf7e.zip
libdecnumber: Introduce decNumberIntegralToInt128
This will be used to implement PowerPC's dctfixqq.

Signed-off-by: Luis Pires <luis.pires@eldorado.org.br>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-Id: <20211029192417.400707-7-luis.pires@eldorado.org.br>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Diffstat (limited to 'libdecnumber/decNumber.c')
-rw-r--r--libdecnumber/decNumber.c95
1 files changed, 95 insertions, 0 deletions
diff --git a/libdecnumber/decNumber.c b/libdecnumber/decNumber.c
index d7716ce175..31282adafd 100644
--- a/libdecnumber/decNumber.c
+++ b/libdecnumber/decNumber.c
@@ -264,6 +264,7 @@ static decNumber * decTrim(decNumber *, decContext *, Flag, Int *);
 static Int	   decUnitAddSub(const Unit *, Int, const Unit *, Int, Int,
 			      Unit *, Int);
 static Int	   decUnitCompare(const Unit *, Int, const Unit *, Int, Int);
+static bool        mulUInt128ByPowOf10(uLong *, uLong *, uInt);
 
 #if !DECSUBSET
 /* decFinish == decFinalize when no subset arithmetic needed */
@@ -542,6 +543,68 @@ Invalid:
     return 0;
 } /* decNumberIntegralToInt64 */
 
+/* ------------------------------------------------------------------ */
+/* decNumberIntegralToInt128 -- conversion to int128                  */
+/*                                                                    */
+/*  dn is the decNumber to convert.  dn is assumed to have been       */
+/*    rounded to a floating point integer value.                      */
+/*  set is the context for reporting errors                           */
+/*  returns the converted decNumber via plow and phigh                */
+/*                                                                    */
+/* Invalid is set if the decNumber is a NaN, Infinite or is out of    */
+/* range for a signed 128 bit integer.                                */
+/* ------------------------------------------------------------------ */
+
+void decNumberIntegralToInt128(const decNumber *dn, decContext *set,
+        uint64_t *plow, uint64_t *phigh)
+{
+    int d;        /* work */
+    const Unit *up;   /* .. */
+    uint64_t lo = 0, hi = 0;
+
+    if (decNumberIsSpecial(dn) || (dn->exponent < 0) ||
+       (dn->digits + dn->exponent > 39)) {
+        goto Invalid;
+    }
+
+    up = dn->lsu;     /* -> lsu */
+
+    for (d = (dn->digits - 1) / DECDPUN; d >= 0; d--) {
+        if (mulu128(&lo, &hi, DECDPUNMAX + 1)) {
+            /* overflow */
+            goto Invalid;
+        }
+        if (uadd64_overflow(lo, up[d], &lo)) {
+            if (uadd64_overflow(hi, 1, &hi)) {
+                /* overflow */
+                goto Invalid;
+            }
+        }
+    }
+
+    if (mulUInt128ByPowOf10(&lo, &hi, dn->exponent)) {
+        /* overflow */
+        goto Invalid;
+    }
+
+    if (decNumberIsNegative(dn)) {
+        if (lo == 0) {
+            *phigh = -hi;
+            *plow = 0;
+        } else {
+            *phigh = ~hi;
+            *plow = -lo;
+        }
+    } else {
+        *plow = lo;
+        *phigh = hi;
+    }
+
+    return;
+
+Invalid:
+    decContextSetStatus(set, DEC_Invalid_operation);
+} /* decNumberIntegralToInt128 */
 
 /* ------------------------------------------------------------------ */
 /* to-scientific-string -- conversion to numeric string		      */
@@ -7885,6 +7948,38 @@ static Int decGetDigits(Unit *uar, Int len) {
   return digits;
   } /* decGetDigits */
 
+/* ------------------------------------------------------------------ */
+/* mulUInt128ByPowOf10 -- multiply a 128-bit unsigned integer by a    */
+/* power of 10.                                                       */
+/*                                                                    */
+/*   The 128-bit factor composed of plow and phigh is multiplied      */
+/*   by 10^exp.                                                       */
+/*                                                                    */
+/*   plow   pointer to the low 64 bits of the first factor            */
+/*   phigh  pointer to the high 64 bits of the first factor           */
+/*   exp    the exponent of the power of 10 of the second factor      */
+/*                                                                    */
+/* If the result fits in 128 bits, returns false and the              */
+/* multiplication result through plow and phigh.                      */
+/* Otherwise, returns true.                                           */
+/* ------------------------------------------------------------------ */
+static bool mulUInt128ByPowOf10(uLong *plow, uLong *phigh, uInt pow10)
+{
+    while (pow10 >= ARRAY_SIZE(powers)) {
+        if (mulu128(plow, phigh, powers[ARRAY_SIZE(powers) - 1])) {
+            /* Overflow */
+            return true;
+        }
+        pow10 -= ARRAY_SIZE(powers) - 1;
+    }
+
+    if (pow10 > 0) {
+        return mulu128(plow, phigh, powers[pow10]);
+    } else {
+        return false;
+    }
+}
+
 #if DECTRACE | DECCHECK
 /* ------------------------------------------------------------------ */
 /* decNumberShow -- display a number [debug aid]		      */