f744a9
From 85ed88218164ceecb360bd9b8539e55076df9d19 Mon Sep 17 00:00:00 2001
f744a9
From: =?UTF-8?q?Nikola=20Forr=C3=B3?= <nforro@redhat.com>
f744a9
Date: Thu, 30 May 2019 15:52:23 +0200
f744a9
Subject: [PATCH] Fix broken float128 on all arches except x86_64
f744a9
f744a9
Backport of the following upstream commits:
f744a9
f744a9
3d21112d6 MAINT: Add bitmask helper functions
f744a9
4d7c529d7 MAINT: Reorganize Dragon4 code to clarify float formats
f744a9
727756e63 MAINT: replace static BigInts in dragon4 by dummy manager
f744a9
888d37086 MAINT: Add comments to long_double detection code
f744a9
cdc6b6847 BUG: Implement float128 dragon4 for IBM double-double (ppc64)
f744a9
718d9e866 ENH: Add support for the 64-bit RISC-V architecture
f744a9
b35dfc2d5 ENH: Add AARCH32 support.
f744a9
3973b2508 Adjust endianness header file to accommodate AARCHXX changes.
f744a9
f07359b22 BLD: Modify cpu detection and function to get build working on aarch64  (#11568)
f744a9
dd949d5c6 BUG: Fix printing of longdouble on ppc64le.
f744a9
641058fb7 MAINT: remove darwin hardcoded LDOUBLE detection
f744a9
ec843c700 BUG: Fix undefined functions on big-endian systems.
f744a9
00bc5bc88 BUG: Fix test sensitive to platform byte order.
f744a9
---
f744a9
 numpy/core/include/numpy/npy_cpu.h          |   30 +-
f744a9
 numpy/core/include/numpy/npy_endian.h       |   44 +-
f744a9
 numpy/core/setup.py                         |   13 +-
f744a9
 numpy/core/setup_common.py                  |   18 +-
f744a9
 numpy/core/src/multiarray/dragon4.c         | 1613 ++++++++++++-------
f744a9
 numpy/core/src/multiarray/dragon4.h         |   66 +-
f744a9
 numpy/core/src/multiarray/scalartypes.c.src |    5 +-
f744a9
 numpy/core/src/npymath/ieee754.c.src        |    3 +-
f744a9
 numpy/core/src/npymath/npy_math_private.h   |    9 +-
f744a9
 numpy/core/src/private/npy_config.h         |    3 +-
f744a9
 numpy/core/src/private/npy_fpmath.h         |   41 +-
f744a9
 numpy/core/tests/test_scalarprint.py        |   63 +-
f744a9
 numpy/core/tests/test_umath.py              |   12 +-
f744a9
 numpy/ma/tests/test_core.py                 |   11 +-
f744a9
 14 files changed, 1277 insertions(+), 654 deletions(-)
f744a9
f744a9
diff --git a/numpy/core/include/numpy/npy_cpu.h b/numpy/core/include/numpy/npy_cpu.h
f744a9
index 84653ea..1109426 100644
f744a9
--- a/numpy/core/include/numpy/npy_cpu.h
f744a9
+++ b/numpy/core/include/numpy/npy_cpu.h
f744a9
@@ -17,6 +17,7 @@
f744a9
  *              NPY_CPU_SH_BE
f744a9
  *              NPY_CPU_ARCEL
f744a9
  *              NPY_CPU_ARCEB
f744a9
+ *              NPY_CPU_RISCV64
f744a9
  */
f744a9
 #ifndef _NPY_CPUARCH_H_
f744a9
 #define _NPY_CPUARCH_H_
f744a9
@@ -60,10 +61,27 @@
f744a9
     #define NPY_CPU_HPPA
f744a9
 #elif defined(__alpha__)
f744a9
     #define NPY_CPU_ALPHA
f744a9
-#elif defined(__arm__) && defined(__ARMEL__)
f744a9
-    #define NPY_CPU_ARMEL
f744a9
-#elif defined(__arm__) && defined(__ARMEB__)
f744a9
-    #define NPY_CPU_ARMEB
f744a9
+#elif defined(__arm__) || defined(__aarch64__)
f744a9
+    #if defined(__ARMEB__) || defined(__AARCH64EB__)
f744a9
+        #if defined(__ARM_32BIT_STATE)
f744a9
+            #define NPY_CPU_ARMEB_AARCH32
f744a9
+        #elif defined(__ARM_64BIT_STATE)
f744a9
+            #define NPY_CPU_ARMEB_AARCH64
f744a9
+        #else
f744a9
+            #define NPY_CPU_ARMEB
f744a9
+        #endif
f744a9
+    #elif defined(__ARMEL__) || defined(__AARCH64EL__)
f744a9
+        #if defined(__ARM_32BIT_STATE)
f744a9
+            #define NPY_CPU_ARMEL_AARCH32
f744a9
+        #elif defined(__ARM_64BIT_STATE)
f744a9
+            #define NPY_CPU_ARMEL_AARCH64
f744a9
+        #else
f744a9
+            #define NPY_CPU_ARMEL
f744a9
+        #endif
f744a9
+    #else
f744a9
+        # error Unknown ARM CPU, please report this to numpy maintainers with \
f744a9
+	information about your platform (OS, CPU and compiler)
f744a9
+    #endif
f744a9
 #elif defined(__sh__) && defined(__LITTLE_ENDIAN__)
f744a9
     #define NPY_CPU_SH_LE
f744a9
 #elif defined(__sh__) && defined(__BIG_ENDIAN__)
f744a9
@@ -74,14 +92,14 @@
f744a9
     #define NPY_CPU_MIPSEB
f744a9
 #elif defined(__or1k__)
f744a9
     #define NPY_CPU_OR1K
f744a9
-#elif defined(__aarch64__)
f744a9
-    #define NPY_CPU_AARCH64
f744a9
 #elif defined(__mc68000__)
f744a9
     #define NPY_CPU_M68K
f744a9
 #elif defined(__arc__) && defined(__LITTLE_ENDIAN__)
f744a9
     #define NPY_CPU_ARCEL
f744a9
 #elif defined(__arc__) && defined(__BIG_ENDIAN__)
f744a9
     #define NPY_CPU_ARCEB
f744a9
+#elif defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 64
f744a9
+    #define NPY_CPU_RISCV64
f744a9
 #else
f744a9
     #error Unknown CPU, please report this to numpy maintainers with \
f744a9
     information about your platform (OS, CPU and compiler)
f744a9
diff --git a/numpy/core/include/numpy/npy_endian.h b/numpy/core/include/numpy/npy_endian.h
f744a9
index 1a42121..44cdffd 100644
f744a9
--- a/numpy/core/include/numpy/npy_endian.h
f744a9
+++ b/numpy/core/include/numpy/npy_endian.h
f744a9
@@ -37,27 +37,31 @@
f744a9
     #define NPY_LITTLE_ENDIAN 1234
f744a9
     #define NPY_BIG_ENDIAN 4321
f744a9
 
f744a9
-    #if defined(NPY_CPU_X86)            \
f744a9
-            || defined(NPY_CPU_AMD64)   \
f744a9
-            || defined(NPY_CPU_IA64)    \
f744a9
-            || defined(NPY_CPU_ALPHA)   \
f744a9
-            || defined(NPY_CPU_ARMEL)   \
f744a9
-            || defined(NPY_CPU_AARCH64) \
f744a9
-            || defined(NPY_CPU_SH_LE)   \
f744a9
-            || defined(NPY_CPU_MIPSEL)  \
f744a9
-            || defined(NPY_CPU_PPC64LE) \
f744a9
-            || defined(NPY_CPU_ARCEL)
f744a9
+    #if defined(NPY_CPU_X86)                  \
f744a9
+            || defined(NPY_CPU_AMD64)         \
f744a9
+            || defined(NPY_CPU_IA64)          \
f744a9
+            || defined(NPY_CPU_ALPHA)         \
f744a9
+            || defined(NPY_CPU_ARMEL)         \
f744a9
+            || defined(NPY_CPU_ARMEL_AARCH32) \
f744a9
+            || defined(NPY_CPU_ARMEL_AARCH64) \
f744a9
+            || defined(NPY_CPU_SH_LE)         \
f744a9
+            || defined(NPY_CPU_MIPSEL)        \
f744a9
+            || defined(NPY_CPU_PPC64LE)       \
f744a9
+            || defined(NPY_CPU_ARCEL)         \
f744a9
+            || defined(NPY_CPU_RISCV64)
f744a9
         #define NPY_BYTE_ORDER NPY_LITTLE_ENDIAN
f744a9
-    #elif defined(NPY_CPU_PPC)          \
f744a9
-            || defined(NPY_CPU_SPARC)   \
f744a9
-            || defined(NPY_CPU_S390)    \
f744a9
-            || defined(NPY_CPU_HPPA)    \
f744a9
-            || defined(NPY_CPU_PPC64)   \
f744a9
-            || defined(NPY_CPU_ARMEB)   \
f744a9
-            || defined(NPY_CPU_SH_BE)   \
f744a9
-            || defined(NPY_CPU_MIPSEB)  \
f744a9
-            || defined(NPY_CPU_OR1K)    \
f744a9
-            || defined(NPY_CPU_M68K)    \
f744a9
+    #elif defined(NPY_CPU_PPC)                \
f744a9
+            || defined(NPY_CPU_SPARC)         \
f744a9
+            || defined(NPY_CPU_S390)          \
f744a9
+            || defined(NPY_CPU_HPPA)          \
f744a9
+            || defined(NPY_CPU_PPC64)         \
f744a9
+            || defined(NPY_CPU_ARMEB)         \
f744a9
+            || defined(NPY_CPU_ARMEB_AARCH32) \
f744a9
+            || defined(NPY_CPU_ARMEB_AARCH64) \
f744a9
+            || defined(NPY_CPU_SH_BE)         \
f744a9
+            || defined(NPY_CPU_MIPSEB)        \
f744a9
+            || defined(NPY_CPU_OR1K)          \
f744a9
+            || defined(NPY_CPU_M68K)          \
f744a9
             || defined(NPY_CPU_ARCEB)
f744a9
         #define NPY_BYTE_ORDER NPY_BIG_ENDIAN
f744a9
     #else
f744a9
diff --git a/numpy/core/setup.py b/numpy/core/setup.py
f744a9
index 371df5b..cc1bb5b 100644
f744a9
--- a/numpy/core/setup.py
f744a9
+++ b/numpy/core/setup.py
f744a9
@@ -452,17 +452,8 @@ def configuration(parent_package='',top_path=None):
f744a9
                 moredefs.append(('NPY_RELAXED_STRIDES_DEBUG', 1))
f744a9
 
f744a9
             # Get long double representation
f744a9
-            if sys.platform != 'darwin':
f744a9
-                rep = check_long_double_representation(config_cmd)
f744a9
-                if rep in ['INTEL_EXTENDED_12_BYTES_LE',
f744a9
-                           'INTEL_EXTENDED_16_BYTES_LE',
f744a9
-                           'MOTOROLA_EXTENDED_12_BYTES_BE',
f744a9
-                           'IEEE_QUAD_LE', 'IEEE_QUAD_BE',
f744a9
-                           'IEEE_DOUBLE_LE', 'IEEE_DOUBLE_BE',
f744a9
-                           'DOUBLE_DOUBLE_BE', 'DOUBLE_DOUBLE_LE']:
f744a9
-                    moredefs.append(('HAVE_LDOUBLE_%s' % rep, 1))
f744a9
-                else:
f744a9
-                    raise ValueError("Unrecognized long double format: %s" % rep)
f744a9
+            rep = check_long_double_representation(config_cmd)
f744a9
+            moredefs.append(('HAVE_LDOUBLE_%s' % rep, 1))
f744a9
 
f744a9
             # Py3K check
f744a9
             if sys.version_info[0] == 3:
f744a9
diff --git a/numpy/core/setup_common.py b/numpy/core/setup_common.py
f744a9
index bd093c5..343318c 100644
f744a9
--- a/numpy/core/setup_common.py
f744a9
+++ b/numpy/core/setup_common.py
f744a9
@@ -333,9 +333,9 @@ _MOTOROLA_EXTENDED_12B = ['300', '031', '000', '000', '353', '171',
f744a9
 _IEEE_QUAD_PREC_BE = ['300', '031', '326', '363', '105', '100', '000', '000',
f744a9
                       '000', '000', '000', '000', '000', '000', '000', '000']
f744a9
 _IEEE_QUAD_PREC_LE = _IEEE_QUAD_PREC_BE[::-1]
f744a9
-_DOUBLE_DOUBLE_BE = (['301', '235', '157', '064', '124', '000', '000', '000'] +
f744a9
+_IBM_DOUBLE_DOUBLE_BE = (['301', '235', '157', '064', '124', '000', '000', '000'] +
f744a9
                      ['000'] * 8)
f744a9
-_DOUBLE_DOUBLE_LE = (['000', '000', '000', '124', '064', '157', '235', '301'] +
f744a9
+_IBM_DOUBLE_DOUBLE_LE = (['000', '000', '000', '124', '064', '157', '235', '301'] +
f744a9
                      ['000'] * 8)
f744a9
 
f744a9
 def long_double_representation(lines):
f744a9
@@ -362,11 +362,16 @@ def long_double_representation(lines):
f744a9
             # the long double
f744a9
             if read[-8:] == _AFTER_SEQ:
f744a9
                 saw = copy.copy(read)
f744a9
+                # if the content was 12 bytes, we only have 32 - 8 - 12 = 12
f744a9
+                # "before" bytes. In other words the first 4 "before" bytes went
f744a9
+                # past the sliding window.
f744a9
                 if read[:12] == _BEFORE_SEQ[4:]:
f744a9
                     if read[12:-8] == _INTEL_EXTENDED_12B:
f744a9
                         return 'INTEL_EXTENDED_12_BYTES_LE'
f744a9
                     if read[12:-8] == _MOTOROLA_EXTENDED_12B:
f744a9
                         return 'MOTOROLA_EXTENDED_12_BYTES_BE'
f744a9
+                # if the content was 16 bytes, we are left with 32-8-16 = 16
f744a9
+                # "before" bytes, so 8 went past the sliding window.
f744a9
                 elif read[:8] == _BEFORE_SEQ[8:]:
f744a9
                     if read[8:-8] == _INTEL_EXTENDED_16B:
f744a9
                         return 'INTEL_EXTENDED_16_BYTES_LE'
f744a9
@@ -374,10 +379,11 @@ def long_double_representation(lines):
f744a9
                         return 'IEEE_QUAD_BE'
f744a9
                     elif read[8:-8] == _IEEE_QUAD_PREC_LE:
f744a9
                         return 'IEEE_QUAD_LE'
f744a9
-                    elif read[8:-8] == _DOUBLE_DOUBLE_BE:
f744a9
-                        return 'DOUBLE_DOUBLE_BE'
f744a9
-                    elif read[8:-8] == _DOUBLE_DOUBLE_LE:
f744a9
-                        return 'DOUBLE_DOUBLE_LE'
f744a9
+                    elif read[8:-8] == _IBM_DOUBLE_DOUBLE_LE:
f744a9
+                        return 'IBM_DOUBLE_DOUBLE_LE'
f744a9
+                    elif read[8:-8] == _IBM_DOUBLE_DOUBLE_BE:
f744a9
+                        return 'IBM_DOUBLE_DOUBLE_BE'
f744a9
+                # if the content was 8 bytes, left with 32-8-8 = 16 bytes
f744a9
                 elif read[:16] == _BEFORE_SEQ:
f744a9
                     if read[16:-8] == _IEEE_DOUBLE_LE:
f744a9
                         return 'IEEE_DOUBLE_LE'
f744a9
diff --git a/numpy/core/src/multiarray/dragon4.c b/numpy/core/src/multiarray/dragon4.c
f744a9
index e005234..e3da79f 100644
f744a9
--- a/numpy/core/src/multiarray/dragon4.c
f744a9
+++ b/numpy/core/src/multiarray/dragon4.c
f744a9
@@ -42,6 +42,18 @@
f744a9
 #define DEBUG_ASSERT(stmnt) do {} while(0)
f744a9
 #endif
f744a9
 
f744a9
+static inline npy_uint64
f744a9
+bitmask_u64(npy_uint32 n)
f744a9
+{
f744a9
+    return ~(~((npy_uint64)0) << n);
f744a9
+}
f744a9
+
f744a9
+static inline npy_uint32
f744a9
+bitmask_u32(npy_uint32 n)
f744a9
+{
f744a9
+    return ~(~((npy_uint32)0) << n);
f744a9
+}
f744a9
+
f744a9
 /*
f744a9
  *  Get the log base 2 of a 32-bit unsigned integer.
f744a9
  *  http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup
f744a9
@@ -102,6 +114,17 @@ LogBase2_64(npy_uint64 val)
f744a9
     return LogBase2_32((npy_uint32)val);
f744a9
 }
f744a9
 
f744a9
+#if defined(HAVE_LDOUBLE_IEEE_QUAD_LE) || defined(HAVE_LDOUBLE_IEEE_QUAD_BE)
f744a9
+static npy_uint32
f744a9
+LogBase2_128(npy_uint64 hi, npy_uint64 lo)
f744a9
+{
f744a9
+    if (hi) {
f744a9
+        return 64 + LogBase2_64(hi);
f744a9
+    }
f744a9
+
f744a9
+    return LogBase2_64(lo);
f744a9
+}
f744a9
+#endif /* HAVE_LDOUBLE_IEEE_QUAD_LE */
f744a9
 
f744a9
 /*
f744a9
  * Maximum number of 32 bit blocks needed in high precision arithmetic to print
f744a9
@@ -122,6 +145,45 @@ typedef struct BigInt {
f744a9
     npy_uint32 blocks[c_BigInt_MaxBlocks];
f744a9
 } BigInt;
f744a9
 
f744a9
+/*
f744a9
+ * Dummy implementation of a memory manager for BigInts. Currently, only
f744a9
+ * supports a single call to Dragon4, but that is OK because Dragon4
f744a9
+ * does not release the GIL.
f744a9
+ *
f744a9
+ * We try to raise an error anyway if dragon4 re-enters, and this code serves
f744a9
+ * as a placeholder if we want to make it re-entrant in the future.
f744a9
+ *
f744a9
+ * Each call to dragon4 uses 7 BigInts.
f744a9
+ */
f744a9
+#define BIGINT_DRAGON4_GROUPSIZE 7
f744a9
+typedef struct {
f744a9
+    BigInt bigints[BIGINT_DRAGON4_GROUPSIZE];
f744a9
+    char repr[16384];
f744a9
+} Dragon4_Scratch;
f744a9
+
f744a9
+static int _bigint_static_in_use = 0;
f744a9
+static Dragon4_Scratch _bigint_static;
f744a9
+
f744a9
+static Dragon4_Scratch*
f744a9
+get_dragon4_bigint_scratch(void) {
f744a9
+    /* this test+set is not threadsafe, but no matter because we have GIL */
f744a9
+    if (_bigint_static_in_use) {
f744a9
+        PyErr_SetString(PyExc_RuntimeError,
f744a9
+            "numpy float printing code is not re-entrant. "
f744a9
+            "Ping the devs to fix it.");
f744a9
+        return NULL;
f744a9
+    }
f744a9
+    _bigint_static_in_use = 1;
f744a9
+
f744a9
+    /* in this dummy implementation we only return the static allocation */
f744a9
+    return &_bigint_static;
f744a9
+}
f744a9
+
f744a9
+static void
f744a9
+free_dragon4_bigint_scratch(Dragon4_Scratch *mem){
f744a9
+    _bigint_static_in_use = 0;
f744a9
+}
f744a9
+
f744a9
 /* Copy integer */
f744a9
 static void
f744a9
 BigInt_Copy(BigInt *dst, const BigInt *src)
f744a9
@@ -139,32 +201,86 @@ BigInt_Copy(BigInt *dst, const BigInt *src)
f744a9
 static void
f744a9
 BigInt_Set_uint64(BigInt *i, npy_uint64 val)
f744a9
 {
f744a9
-    if (val > 0xFFFFFFFF) {
f744a9
-        i->blocks[0] = val & 0xFFFFFFFF;
f744a9
-        i->blocks[1] = (val >> 32) & 0xFFFFFFFF;
f744a9
+    if (val > bitmask_u64(32)) {
f744a9
+        i->blocks[0] = val & bitmask_u64(32);
f744a9
+        i->blocks[1] = (val >> 32) & bitmask_u64(32);
f744a9
         i->length = 2;
f744a9
     }
f744a9
     else if (val != 0) {
f744a9
-        i->blocks[0] = val & 0xFFFFFFFF;
f744a9
+        i->blocks[0] = val & bitmask_u64(32);
f744a9
+        i->length = 1;
f744a9
+    }
f744a9
+    else {
f744a9
+        i->length = 0;
f744a9
+    }
f744a9
+}
f744a9
+
f744a9
+#if (defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_LE) || \
f744a9
+     defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE) || \
f744a9
+     defined(HAVE_LDOUBLE_IEEE_QUAD_LE) || \
f744a9
+     defined(HAVE_LDOUBLE_IEEE_QUAD_BE))
f744a9
+static void
f744a9
+BigInt_Set_2x_uint64(BigInt *i, npy_uint64 hi, npy_uint64 lo)
f744a9
+{
f744a9
+    if (hi > bitmask_u64(32)) {
f744a9
+        i->length = 4;
f744a9
+    }
f744a9
+    else if (hi != 0) {
f744a9
+        i->length = 3;
f744a9
+    }
f744a9
+    else if (lo > bitmask_u64(32)) {
f744a9
+        i->length = 2;
f744a9
+    }
f744a9
+    else if (lo != 0) {
f744a9
         i->length = 1;
f744a9
     }
f744a9
     else {
f744a9
         i->length = 0;
f744a9
     }
f744a9
+
f744a9
+    switch (i->length) {
f744a9
+        case 4:
f744a9
+            i->blocks[3] = (hi >> 32) & bitmask_u64(32);
f744a9
+        case 3:
f744a9
+            i->blocks[2] = hi & bitmask_u64(32);
f744a9
+        case 2:
f744a9
+            i->blocks[1] = (lo >> 32) & bitmask_u64(32);
f744a9
+        case 1:
f744a9
+            i->blocks[0] = lo & bitmask_u64(32);
f744a9
+    }
f744a9
 }
f744a9
+#endif /* DOUBLE_DOUBLE and QUAD */
f744a9
 
f744a9
 static void
f744a9
 BigInt_Set_uint32(BigInt *i, npy_uint32 val)
f744a9
 {
f744a9
     if (val != 0) {
f744a9
         i->blocks[0] = val;
f744a9
-        i->length = (val != 0);
f744a9
+        i->length = 1;
f744a9
     }
f744a9
     else {
f744a9
         i->length = 0;
f744a9
     }
f744a9
 }
f744a9
 
f744a9
+/*
f744a9
+ * Returns 1 if the value is zero
f744a9
+ */
f744a9
+static int
f744a9
+BigInt_IsZero(const BigInt *i)
f744a9
+{
f744a9
+    return i->length == 0;
f744a9
+}
f744a9
+
f744a9
+/*
f744a9
+ * Returns 1 if the value is even
f744a9
+ */
f744a9
+static int
f744a9
+BigInt_IsEven(const BigInt *i)
f744a9
+{
f744a9
+    return (i->length == 0) || ( (i->blocks[0] % 2) == 0);
f744a9
+}
f744a9
+
f744a9
 /*
f744a9
  * Returns 0 if (lhs = rhs), negative if (lhs < rhs), positive if (lhs > rhs)
f744a9
  */
f744a9
@@ -228,7 +344,7 @@ BigInt_Add(BigInt *result, const BigInt *lhs, const BigInt *rhs)
f744a9
         npy_uint64 sum = carry + (npy_uint64)(*largeCur) +
f744a9
                                  (npy_uint64)(*smallCur);
f744a9
         carry = sum >> 32;
f744a9
-        *resultCur = sum & 0xFFFFFFFF;
f744a9
+        *resultCur = sum & bitmask_u64(32);
f744a9
         ++largeCur;
f744a9
         ++smallCur;
f744a9
         ++resultCur;
f744a9
@@ -238,7 +354,7 @@ BigInt_Add(BigInt *result, const BigInt *lhs, const BigInt *rhs)
f744a9
     while (largeCur != largeEnd) {
f744a9
         npy_uint64 sum = carry + (npy_uint64)(*largeCur);
f744a9
         carry = sum >> 32;
f744a9
-        (*resultCur) = sum & 0xFFFFFFFF;
f744a9
+        (*resultCur) = sum & bitmask_u64(32);
f744a9
         ++largeCur;
f744a9
         ++resultCur;
f744a9
     }
f744a9
@@ -307,13 +423,13 @@ BigInt_Multiply(BigInt *result, const BigInt *lhs, const BigInt *rhs)
f744a9
                 npy_uint64 product = (*resultCur) +
f744a9
                                      (*largeCur)*(npy_uint64)multiplier + carry;
f744a9
                 carry = product >> 32;
f744a9
-                *resultCur = product & 0xFFFFFFFF;
f744a9
+                *resultCur = product & bitmask_u64(32);
f744a9
                 ++largeCur;
f744a9
                 ++resultCur;
f744a9
             } while(largeCur != large->blocks + large->length);
f744a9
 
f744a9
             DEBUG_ASSERT(resultCur < result->blocks + maxResultLen);
f744a9
-            *resultCur = (npy_uint32)(carry & 0xFFFFFFFF);
f744a9
+            *resultCur = (npy_uint32)(carry & bitmask_u64(32));
f744a9
         }
f744a9
     }
f744a9
 
f744a9
@@ -337,7 +453,7 @@ BigInt_Multiply_int(BigInt *result, const BigInt *lhs, npy_uint32 rhs)
f744a9
     const npy_uint32 *pLhsEnd = lhs->blocks + lhs->length;
f744a9
     for ( ; pLhsCur != pLhsEnd; ++pLhsCur, ++resultCur) {
f744a9
         npy_uint64 product = (npy_uint64)(*pLhsCur) * rhs + carry;
f744a9
-        *resultCur = (npy_uint32)(product & 0xFFFFFFFF);
f744a9
+        *resultCur = (npy_uint32)(product & bitmask_u64(32));
f744a9
         carry = product >> 32;
f744a9
     }
f744a9
 
f744a9
@@ -414,7 +530,7 @@ BigInt_Multiply10(BigInt *result)
f744a9
     npy_uint32 *end = result->blocks + result->length;
f744a9
     for ( ; cur != end; ++cur) {
f744a9
         npy_uint64 product = (npy_uint64)(*cur) * 10ull + carry;
f744a9
-        (*cur) = (npy_uint32)(product & 0xFFFFFFFF);
f744a9
+        (*cur) = (npy_uint32)(product & bitmask_u64(32));
f744a9
         carry = product >> 32;
f744a9
     }
f744a9
 
f744a9
@@ -637,13 +753,11 @@ static BigInt g_PowerOf10_Big[] =
f744a9
 
f744a9
 /* result = 10^exponent */
f744a9
 static void
f744a9
-BigInt_Pow10(BigInt *result, npy_uint32 exponent)
f744a9
+BigInt_Pow10(BigInt *result, npy_uint32 exponent, BigInt *temp)
f744a9
 {
f744a9
-    /* create two temporary values to reduce large integer copy operations */
f744a9
-    BigInt temp1;
f744a9
-    BigInt temp2;
f744a9
-    BigInt *curTemp = &temp1;
f744a9
-    BigInt *pNextTemp = &temp2;
f744a9
+    /* use two temporary values to reduce large integer copy operations */
f744a9
+    BigInt *curTemp = result;
f744a9
+    BigInt *pNextTemp = temp;
f744a9
     npy_uint32 smallExponent;
f744a9
     npy_uint32 tableIdx = 0;
f744a9
 
f744a9
@@ -654,7 +768,7 @@ BigInt_Pow10(BigInt *result, npy_uint32 exponent)
f744a9
      * initialize the result by looking up a 32-bit power of 10 corresponding to
f744a9
      * the first 3 bits
f744a9
      */
f744a9
-    smallExponent = exponent & 0x7;
f744a9
+    smallExponent = exponent & bitmask_u32(3);
f744a9
     BigInt_Set_uint32(curTemp, g_PowerOf10_U32[smallExponent]);
f744a9
 
f744a9
     /* remove the low bits that we used for the 32-bit lookup table */
f744a9
@@ -681,19 +795,17 @@ BigInt_Pow10(BigInt *result, npy_uint32 exponent)
f744a9
     }
f744a9
 
f744a9
     /* output the result */
f744a9
-    BigInt_Copy(result, curTemp);
f744a9
+    if (curTemp != result) {
f744a9
+        BigInt_Copy(result, curTemp);
f744a9
+    }
f744a9
 }
f744a9
 
f744a9
-/* result = in * 10^exponent */
f744a9
+/* in = in * 10^exponent */
f744a9
 static void
f744a9
-BigInt_MultiplyPow10(BigInt *result, const BigInt *in, npy_uint32 exponent)
f744a9
+BigInt_MultiplyPow10(BigInt *in, npy_uint32 exponent, BigInt *temp)
f744a9
 {
f744a9
-
f744a9
-    /* create two temporary values to reduce large integer copy operations */
f744a9
-    BigInt temp1;
f744a9
-    BigInt temp2;
f744a9
-    BigInt *curTemp = &temp1;
f744a9
-    BigInt *pNextTemp = &temp2;
f744a9
+    /* use two temporary values to reduce large integer copy operations */
f744a9
+    BigInt *curTemp, *pNextTemp;
f744a9
     npy_uint32 smallExponent;
f744a9
     npy_uint32 tableIdx = 0;
f744a9
 
f744a9
@@ -704,12 +816,15 @@ BigInt_MultiplyPow10(BigInt *result, const BigInt *in, npy_uint32 exponent)
f744a9
      * initialize the result by looking up a 32-bit power of 10 corresponding to
f744a9
      * the first 3 bits
f744a9
      */
f744a9
-    smallExponent = exponent & 0x7;
f744a9
+    smallExponent = exponent & bitmask_u32(3);
f744a9
     if (smallExponent != 0) {
f744a9
-        BigInt_Multiply_int(curTemp, in, g_PowerOf10_U32[smallExponent]);
f744a9
+        BigInt_Multiply_int(temp, in, g_PowerOf10_U32[smallExponent]);
f744a9
+        curTemp = temp;
f744a9
+        pNextTemp = in;
f744a9
     }
f744a9
     else {
f744a9
-        BigInt_Copy(curTemp, in);
f744a9
+        curTemp = in;
f744a9
+        pNextTemp = temp;
f744a9
     }
f744a9
 
f744a9
     /* remove the low bits that we used for the 32-bit lookup table */
f744a9
@@ -724,7 +839,7 @@ BigInt_MultiplyPow10(BigInt *result, const BigInt *in, npy_uint32 exponent)
f744a9
             /* multiply into the next temporary */
f744a9
             BigInt_Multiply(pNextTemp, curTemp, &g_PowerOf10_Big[tableIdx]);
f744a9
 
f744a9
-            // swap to the next temporary
f744a9
+            /* swap to the next temporary */
f744a9
             pSwap = curTemp;
f744a9
             curTemp = pNextTemp;
f744a9
             pNextTemp = pSwap;
f744a9
@@ -736,7 +851,9 @@ BigInt_MultiplyPow10(BigInt *result, const BigInt *in, npy_uint32 exponent)
f744a9
     }
f744a9
 
f744a9
     /* output the result */
f744a9
-    BigInt_Copy(result, curTemp);
f744a9
+    if (curTemp != in){
f744a9
+        BigInt_Copy(in, curTemp);
f744a9
+    }
f744a9
 }
f744a9
 
f744a9
 /* result = 2^exponent */
f744a9
@@ -788,7 +905,7 @@ BigInt_DivideWithRemainder_MaxQuotient9(BigInt *dividend, const BigInt *divisor)
f744a9
      */
f744a9
     DEBUG_ASSERT(!divisor->length == 0 &&
f744a9
                 divisor->blocks[divisor->length-1] >= 8 &&
f744a9
-                divisor->blocks[divisor->length-1] < 0xFFFFFFFF &&
f744a9
+                divisor->blocks[divisor->length-1] < bitmask_u64(32) &&
f744a9
                 dividend->length <= divisor->length);
f744a9
 
f744a9
     /*
f744a9
@@ -825,10 +942,10 @@ BigInt_DivideWithRemainder_MaxQuotient9(BigInt *dividend, const BigInt *divisor)
f744a9
             carry = product >> 32;
f744a9
 
f744a9
             difference = (npy_uint64)*dividendCur
f744a9
-                       - (product & 0xFFFFFFFF) - borrow;
f744a9
+                       - (product & bitmask_u64(32)) - borrow;
f744a9
             borrow = (difference >> 32) & 1;
f744a9
 
f744a9
-            *dividendCur = difference & 0xFFFFFFFF;
f744a9
+            *dividendCur = difference & bitmask_u64(32);
f744a9
 
f744a9
             ++divisorCur;
f744a9
             ++dividendCur;
f744a9
@@ -860,7 +977,7 @@ BigInt_DivideWithRemainder_MaxQuotient9(BigInt *dividend, const BigInt *divisor)
f744a9
                                   - (npy_uint64)*divisorCur - borrow;
f744a9
             borrow = (difference >> 32) & 1;
f744a9
 
f744a9
-            *dividendCur = difference & 0xFFFFFFFF;
f744a9
+            *dividendCur = difference & bitmask_u64(32);
f744a9
 
f744a9
             ++divisorCur;
f744a9
             ++dividendCur;
f744a9
@@ -993,12 +1110,20 @@ BigInt_ShiftLeft(BigInt *result, npy_uint32 shift)
f744a9
  * There is some more documentation of these changes on Ryan Juckett's website
f744a9
  * at http://www.ryanjuckett.com/programming/printing-floating-point-numbers/
f744a9
  *
f744a9
- * Ryan Juckett's implementation did not implement "IEEE unbiased rounding",
f744a9
- * except in the last digit. This has been added back, following the Burger &
f744a9
- * Dybvig code, using the isEven variable.
f744a9
+ * This code also has a few implementation differences from Ryan Juckett's
f744a9
+ * version:
f744a9
+ *  1. fixed overflow problems when mantissa was 64 bits (in float128 types),
f744a9
+ *     by replacing multiplication by 2 or 4 by BigInt_ShiftLeft calls.
f744a9
+ *  2. Increased c_BigInt_MaxBlocks, for 128-bit floats
f744a9
+ *  3. Added more entries to the g_PowerOf10_Big table, for 128-bit floats.
f744a9
+ *  4. Added unbiased rounding calculation with isEven. Ryan Juckett's
f744a9
+ *     implementation did not implement "IEEE unbiased rounding", except in the
f744a9
+ *     last digit. This has been added back, following the Burger & Dybvig
f744a9
+ *     code, using the isEven variable.
f744a9
  *
f744a9
  * Arguments:
f744a9
- *   * mantissa - value significand
f744a9
+ *   * bigints - memory to store all bigints needed (7) for dragon4 computation.
f744a9
+ *               The first BigInt should be filled in with the mantissa.
f744a9
  *   * exponent - value exponent in base 2
f744a9
  *   * mantissaBit - index of the highest set mantissa bit
f744a9
  *   * hasUnequalMargins - is the high margin twice as large as the low margin
f744a9
@@ -1007,9 +1132,11 @@ BigInt_ShiftLeft(BigInt *result, npy_uint32 shift)
f744a9
  *   * pOutBuffer - buffer to output into
f744a9
  *   * bufferSize - maximum characters that can be printed to pOutBuffer
f744a9
  *   * pOutExponent - the base 10 exponent of the first digit
f744a9
+ *
f744a9
+ * Returns the number of digits written to the output buffer.
f744a9
  */
f744a9
 static npy_uint32
f744a9
-Dragon4(const npy_uint64 mantissa, const npy_int32 exponent,
f744a9
+Dragon4(BigInt *bigints, const npy_int32 exponent,
f744a9
         const npy_uint32 mantissaBit, const npy_bool hasUnequalMargins,
f744a9
         const DigitMode digitMode, const CutoffMode cutoffMode,
f744a9
         npy_int32 cutoffNumber, char *pOutBuffer,
f744a9
@@ -1025,21 +1152,24 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent,
f744a9
      * Here, marginLow and marginHigh represent 1/2 of the distance to the next
f744a9
      * floating point value above/below the mantissa.
f744a9
      *
f744a9
-     * scaledMarginHigh is a pointer so that it can point to scaledMarginLow in
f744a9
-     * the case they must be equal to each other, otherwise it will point to
f744a9
-     * optionalMarginHigh.
f744a9
+     * scaledMarginHigh will point to scaledMarginLow in the case they must be
f744a9
+     * equal to each other, otherwise it will point to optionalMarginHigh.
f744a9
      */
f744a9
-    BigInt scale;
f744a9
-    BigInt scaledValue;
f744a9
-    BigInt scaledMarginLow;
f744a9
+    BigInt *mantissa = &bigints[0];  /* the only initialized bigint */
f744a9
+    BigInt *scale = &bigints[1];
f744a9
+    BigInt *scaledValue = &bigints[2];
f744a9
+    BigInt *scaledMarginLow = &bigints[3];
f744a9
     BigInt *scaledMarginHigh;
f744a9
-    BigInt optionalMarginHigh;
f744a9
+    BigInt *optionalMarginHigh = &bigints[4];
f744a9
+
f744a9
+    BigInt *temp1 = &bigints[5];
f744a9
+    BigInt *temp2 = &bigints[6];
f744a9
 
f744a9
     const npy_float64 log10_2 = 0.30102999566398119521373889472449;
f744a9
     npy_int32 digitExponent, cutoffExponent, hiBlock;
f744a9
     npy_uint32 outputDigit;    /* current digit being output */
f744a9
     npy_uint32 outputLen;
f744a9
-    npy_bool isEven = (mantissa % 2) == 0;
f744a9
+    npy_bool isEven = BigInt_IsEven(mantissa);
f744a9
     npy_int32 cmp;
f744a9
 
f744a9
     /* values used to determine how to round */
f744a9
@@ -1048,12 +1178,14 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent,
f744a9
     DEBUG_ASSERT(bufferSize > 0);
f744a9
 
f744a9
     /* if the mantissa is zero, the value is zero regardless of the exponent */
f744a9
-    if (mantissa == 0) {
f744a9
+    if (BigInt_IsZero(mantissa)) {
f744a9
         *curDigit = '0';
f744a9
         *pOutExponent = 0;
f744a9
         return 1;
f744a9
     }
f744a9
 
f744a9
+    BigInt_Copy(scaledValue, mantissa);
f744a9
+
f744a9
     if (hasUnequalMargins) {
f744a9
         /* if we have no fractional component */
f744a9
         if (exponent > 0) {
f744a9
@@ -1067,17 +1199,13 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent,
f744a9
              */
f744a9
 
f744a9
             /* scaledValue      = 2 * 2 * mantissa*2^exponent */
f744a9
-            BigInt_Set_uint64(&scaledValue, mantissa);
f744a9
-            BigInt_ShiftLeft(&scaledValue, exponent + 2);
f744a9
-
f744a9
+            BigInt_ShiftLeft(scaledValue, exponent + 2);
f744a9
             /* scale            = 2 * 2 * 1 */
f744a9
-            BigInt_Set_uint32(&scale,  4);
f744a9
-
f744a9
+            BigInt_Set_uint32(scale,  4);
f744a9
             /* scaledMarginLow  = 2 * 2^(exponent-1) */
f744a9
-            BigInt_Pow2(&scaledMarginLow, exponent);
f744a9
-
f744a9
+            BigInt_Pow2(scaledMarginLow, exponent);
f744a9
             /* scaledMarginHigh = 2 * 2 * 2^(exponent-1) */
f744a9
-            BigInt_Pow2(&optionalMarginHigh, exponent + 1);
f744a9
+            BigInt_Pow2(optionalMarginHigh, exponent + 1);
f744a9
         }
f744a9
         /* else we have a fractional exponent */
f744a9
         else {
f744a9
@@ -1087,34 +1215,27 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent,
f744a9
              */
f744a9
 
f744a9
             /* scaledValue      = 2 * 2 * mantissa */
f744a9
-            BigInt_Set_uint64(&scaledValue, mantissa);
f744a9
-            BigInt_ShiftLeft(&scaledValue, 2);
f744a9
-
f744a9
+            BigInt_ShiftLeft(scaledValue, 2);
f744a9
             /* scale            = 2 * 2 * 2^(-exponent) */
f744a9
-            BigInt_Pow2(&scale, -exponent + 2);
f744a9
-
f744a9
+            BigInt_Pow2(scale, -exponent + 2);
f744a9
             /* scaledMarginLow  = 2 * 2^(-1) */
f744a9
-            BigInt_Set_uint32(&scaledMarginLow, 1);
f744a9
-
f744a9
+            BigInt_Set_uint32(scaledMarginLow, 1);
f744a9
             /* scaledMarginHigh = 2 * 2 * 2^(-1) */
f744a9
-            BigInt_Set_uint32(&optionalMarginHigh, 2);
f744a9
+            BigInt_Set_uint32(optionalMarginHigh, 2);
f744a9
         }
f744a9
 
f744a9
         /* the high and low margins are different */
f744a9
-        scaledMarginHigh = &optionalMarginHigh;
f744a9
+        scaledMarginHigh = optionalMarginHigh;
f744a9
     }
f744a9
     else {
f744a9
         /* if we have no fractional component */
f744a9
         if (exponent > 0) {
f744a9
             /* scaledValue     = 2 * mantissa*2^exponent */
f744a9
-            BigInt_Set_uint64(&scaledValue, mantissa);
f744a9
-            BigInt_ShiftLeft(&scaledValue, exponent + 1);
f744a9
-
f744a9
+            BigInt_ShiftLeft(scaledValue, exponent + 1);
f744a9
             /* scale           = 2 * 1 */
f744a9
-            BigInt_Set_uint32(&scale, 2);
f744a9
-
f744a9
+            BigInt_Set_uint32(scale, 2);
f744a9
             /* scaledMarginLow = 2 * 2^(exponent-1) */
f744a9
-            BigInt_Pow2(&scaledMarginLow, exponent);
f744a9
+            BigInt_Pow2(scaledMarginLow, exponent);
f744a9
         }
f744a9
         /* else we have a fractional exponent */
f744a9
         else {
f744a9
@@ -1124,18 +1245,15 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent,
f744a9
              */
f744a9
 
f744a9
             /* scaledValue     = 2 * mantissa */
f744a9
-            BigInt_Set_uint64(&scaledValue, mantissa);
f744a9
-            BigInt_ShiftLeft(&scaledValue, 1);
f744a9
-
f744a9
+            BigInt_ShiftLeft(scaledValue, 1);
f744a9
             /* scale           = 2 * 2^(-exponent) */
f744a9
-            BigInt_Pow2(&scale, -exponent + 1);
f744a9
-
f744a9
+            BigInt_Pow2(scale, -exponent + 1);
f744a9
             /* scaledMarginLow = 2 * 2^(-1) */
f744a9
-            BigInt_Set_uint32(&scaledMarginLow, 1);
f744a9
+            BigInt_Set_uint32(scaledMarginLow, 1);
f744a9
         }
f744a9
 
f744a9
         /* the high and low margins are equal */
f744a9
-        scaledMarginHigh = &scaledMarginLow;
f744a9
+        scaledMarginHigh = scaledMarginLow;
f744a9
     }
f744a9
 
f744a9
     /*
f744a9
@@ -1158,6 +1276,9 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent,
f744a9
      *                                                 <= log10(v) + log10(2)
f744a9
      *  floor(log10(v)) < ceil((mantissaBit + exponent) * log10(2))
f744a9
      *                                                 <= floor(log10(v)) + 1
f744a9
+     *
f744a9
+     *  Warning: This calculation assumes npy_float64 is an IEEE-binary64
f744a9
+     *  float. This line may need to be updated if this is not the case.
f744a9
      */
f744a9
     digitExponent = (npy_int32)(
f744a9
        ceil((npy_float64)((npy_int32)mantissaBit + exponent) * log10_2 - 0.69));
f744a9
@@ -1179,31 +1300,29 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent,
f744a9
     /* Divide value by 10^digitExponent. */
f744a9
     if (digitExponent > 0) {
f744a9
         /* A positive exponent creates a division so we multiply the scale. */
f744a9
-        BigInt temp;
f744a9
-        BigInt_MultiplyPow10(&temp, &scale, digitExponent);
f744a9
-        BigInt_Copy(&scale, &temp);
f744a9
+        BigInt_MultiplyPow10(scale, digitExponent, temp1);
f744a9
     }
f744a9
     else if (digitExponent < 0) {
f744a9
         /*
f744a9
          * A negative exponent creates a multiplication so we multiply up the
f744a9
          * scaledValue, scaledMarginLow and scaledMarginHigh.
f744a9
          */
f744a9
-        BigInt pow10, temp;
f744a9
-        BigInt_Pow10(&pow10, -digitExponent);
f744a9
+        BigInt *temp=temp1, *pow10=temp2;
f744a9
+        BigInt_Pow10(pow10, -digitExponent, temp);
f744a9
 
f744a9
-        BigInt_Multiply(&temp, &scaledValue, &pow10);
f744a9
-        BigInt_Copy(&scaledValue, &temp);
f744a9
+        BigInt_Multiply(temp, scaledValue, pow10);
f744a9
+        BigInt_Copy(scaledValue, temp);
f744a9
 
f744a9
-        BigInt_Multiply(&temp, &scaledMarginLow, &pow10);
f744a9
-        BigInt_Copy(&scaledMarginLow, &temp);
f744a9
+        BigInt_Multiply(temp, scaledMarginLow, pow10);
f744a9
+        BigInt_Copy(scaledMarginLow, temp);
f744a9
 
f744a9
-        if (scaledMarginHigh != &scaledMarginLow) {
f744a9
-            BigInt_Multiply2(scaledMarginHigh, &scaledMarginLow);
f744a9
+        if (scaledMarginHigh != scaledMarginLow) {
f744a9
+            BigInt_Multiply2(scaledMarginHigh, scaledMarginLow);
f744a9
         }
f744a9
     }
f744a9
 
f744a9
     /* If (value >= 1), our estimate for digitExponent was too low */
f744a9
-    if (BigInt_Compare(&scaledValue, &scale) >= 0) {
f744a9
+    if (BigInt_Compare(scaledValue, scale) >= 0) {
f744a9
         /*
f744a9
          * The exponent estimate was incorrect.
f744a9
          * Increment the exponent and don't perform the premultiply needed
f744a9
@@ -1217,10 +1336,10 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent,
f744a9
          * Multiply larger by the output base to prepare for the first loop
f744a9
          * iteration.
f744a9
          */
f744a9
-        BigInt_Multiply10(&scaledValue);
f744a9
-        BigInt_Multiply10(&scaledMarginLow);
f744a9
-        if (scaledMarginHigh != &scaledMarginLow) {
f744a9
-            BigInt_Multiply2(scaledMarginHigh, &scaledMarginLow);
f744a9
+        BigInt_Multiply10(scaledValue);
f744a9
+        BigInt_Multiply10(scaledMarginLow);
f744a9
+        if (scaledMarginHigh != scaledMarginLow) {
f744a9
+            BigInt_Multiply2(scaledMarginHigh, scaledMarginLow);
f744a9
         }
f744a9
     }
f744a9
 
f744a9
@@ -1261,8 +1380,8 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent,
f744a9
      * to be less than or equal to 429496729 which is the highest number that
f744a9
      * can be multiplied by 10 without overflowing to a new block.
f744a9
      */
f744a9
-    DEBUG_ASSERT(scale.length > 0);
f744a9
-    hiBlock = scale.blocks[scale.length - 1];
f744a9
+    DEBUG_ASSERT(scale->length > 0);
f744a9
+    hiBlock = scale->blocks[scale->length - 1];
f744a9
     if (hiBlock < 8 || hiBlock > 429496729) {
f744a9
         npy_uint32 hiBlockLog2, shift;
f744a9
 
f744a9
@@ -1280,11 +1399,11 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent,
f744a9
         DEBUG_ASSERT(hiBlockLog2 < 3 || hiBlockLog2 > 27);
f744a9
         shift = (32 + 27 - hiBlockLog2) % 32;
f744a9
 
f744a9
-        BigInt_ShiftLeft(&scale, shift);
f744a9
-        BigInt_ShiftLeft(&scaledValue, shift);
f744a9
-        BigInt_ShiftLeft(&scaledMarginLow, shift);
f744a9
-        if (scaledMarginHigh != &scaledMarginLow) {
f744a9
-            BigInt_Multiply2(scaledMarginHigh, &scaledMarginLow);
f744a9
+        BigInt_ShiftLeft(scale, shift);
f744a9
+        BigInt_ShiftLeft(scaledValue, shift);
f744a9
+        BigInt_ShiftLeft(scaledMarginLow, shift);
f744a9
+        if (scaledMarginHigh != scaledMarginLow) {
f744a9
+            BigInt_Multiply2(scaledMarginHigh, scaledMarginLow);
f744a9
         }
f744a9
     }
f744a9
 
f744a9
@@ -1296,25 +1415,25 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent,
f744a9
          * terminate early.
f744a9
          */
f744a9
         for (;;) {
f744a9
-            BigInt scaledValueHigh;
f744a9
+            BigInt *scaledValueHigh = temp1;
f744a9
 
f744a9
             digitExponent = digitExponent-1;
f744a9
 
f744a9
             /* divide out the scale to extract the digit */
f744a9
             outputDigit =
f744a9
-                BigInt_DivideWithRemainder_MaxQuotient9(&scaledValue, &scale);
f744a9
+                BigInt_DivideWithRemainder_MaxQuotient9(scaledValue, scale);
f744a9
             DEBUG_ASSERT(outputDigit < 10);
f744a9
 
f744a9
             /* update the high end of the value */
f744a9
-            BigInt_Add(&scaledValueHigh, &scaledValue, scaledMarginHigh);
f744a9
+            BigInt_Add(scaledValueHigh, scaledValue, scaledMarginHigh);
f744a9
 
f744a9
             /*
f744a9
              * stop looping if we are far enough away from our neighboring
f744a9
              * values or if we have reached the cutoff digit
f744a9
              */
f744a9
-            cmp = BigInt_Compare(&scaledValue, &scaledMarginLow);
f744a9
+            cmp = BigInt_Compare(scaledValue, scaledMarginLow);
f744a9
             low = isEven ? (cmp <= 0) : (cmp < 0);
f744a9
-            cmp = BigInt_Compare(&scaledValueHigh, &scale);
f744a9
+            cmp = BigInt_Compare(scaledValueHigh, scale);
f744a9
             high = isEven ? (cmp >= 0) : (cmp > 0);
f744a9
             if (low | high | (digitExponent == cutoffExponent))
f744a9
                 break;
f744a9
@@ -1324,10 +1443,10 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent,
f744a9
             ++curDigit;
f744a9
 
f744a9
             /* multiply larger by the output base */
f744a9
-            BigInt_Multiply10(&scaledValue);
f744a9
-            BigInt_Multiply10(&scaledMarginLow);
f744a9
-            if (scaledMarginHigh != &scaledMarginLow) {
f744a9
-                BigInt_Multiply2(scaledMarginHigh, &scaledMarginLow);
f744a9
+            BigInt_Multiply10(scaledValue);
f744a9
+            BigInt_Multiply10(scaledMarginLow);
f744a9
+            if (scaledMarginHigh != scaledMarginLow) {
f744a9
+                BigInt_Multiply2(scaledMarginHigh, scaledMarginLow);
f744a9
             }
f744a9
         }
f744a9
     }
f744a9
@@ -1345,10 +1464,11 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent,
f744a9
 
f744a9
             /* divide out the scale to extract the digit */
f744a9
             outputDigit =
f744a9
-                BigInt_DivideWithRemainder_MaxQuotient9(&scaledValue, &scale);
f744a9
+                BigInt_DivideWithRemainder_MaxQuotient9(scaledValue, scale);
f744a9
             DEBUG_ASSERT(outputDigit < 10);
f744a9
 
f744a9
-            if ((scaledValue.length == 0) | (digitExponent == cutoffExponent)) {
f744a9
+            if ((scaledValue->length == 0) |
f744a9
+                    (digitExponent == cutoffExponent)) {
f744a9
                 break;
f744a9
             }
f744a9
 
f744a9
@@ -1357,7 +1477,7 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent,
f744a9
             ++curDigit;
f744a9
 
f744a9
             /* multiply larger by the output base */
f744a9
-            BigInt_Multiply10(&scaledValue);
f744a9
+            BigInt_Multiply10(scaledValue);
f744a9
         }
f744a9
     }
f744a9
 
f744a9
@@ -1375,8 +1495,8 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent,
f744a9
          *  compare( scale * value, scale * 0.5 )
f744a9
          *  compare( 2 * scale * value, scale )
f744a9
          */
f744a9
-        BigInt_Multiply2_inplace(&scaledValue);
f744a9
-        compare = BigInt_Compare(&scaledValue, &scale);
f744a9
+        BigInt_Multiply2_inplace(scaledValue);
f744a9
+        compare = BigInt_Compare(scaledValue, scale);
f744a9
         roundDown = compare < 0;
f744a9
 
f744a9
         /*
f744a9
@@ -1431,134 +1551,53 @@ Dragon4(const npy_uint64 mantissa, const npy_int32 exponent,
f744a9
 
f744a9
 
f744a9
 /*
f744a9
- * Helper union to decompose a 16-bit IEEE float.
f744a9
- * sign:      1 bit
f744a9
- * exponent:  5 bits
f744a9
- * mantissa: 10 bits
f744a9
- */
f744a9
-typedef union FloatUnion16
f744a9
-{
f744a9
-    npy_uint16 integer;
f744a9
-} FloatUnion16;
f744a9
-
f744a9
-npy_bool   IsNegative_F16(FloatUnion16 *v) { return (v->integer >> 15) != 0; }
f744a9
-npy_uint32 GetExponent_F16(FloatUnion16 *v) { return (v->integer >> 10) & 0x1F;}
f744a9
-npy_uint32 GetMantissa_F16(FloatUnion16 *v) { return v->integer & 0x3FF; }
f744a9
-
f744a9
-
f744a9
-/*
f744a9
- * Helper union to decompose a 32-bit IEEE float.
f744a9
- * sign:      1 bit
f744a9
- * exponent:  8 bits
f744a9
- * mantissa: 23 bits
f744a9
- */
f744a9
-typedef union FloatUnion32
f744a9
-{
f744a9
-    npy_float32 floatingPoint;
f744a9
-    npy_uint32 integer;
f744a9
-} FloatUnion32;
f744a9
-
f744a9
-npy_bool   IsNegative_F32(FloatUnion32 *v) { return (v->integer >> 31) != 0; }
f744a9
-npy_uint32 GetExponent_F32(FloatUnion32 *v) { return (v->integer >> 23) & 0xFF;}
f744a9
-npy_uint32 GetMantissa_F32(FloatUnion32 *v) { return v->integer & 0x7FFFFF; }
f744a9
-
f744a9
-/*
f744a9
- * Helper union to decompose a 64-bit IEEE float.
f744a9
- * sign:      1 bit
f744a9
- * exponent: 11 bits
f744a9
- * mantissa: 52 bits
f744a9
- */
f744a9
-typedef union FloatUnion64
f744a9
-{
f744a9
-    npy_float64 floatingPoint;
f744a9
-    npy_uint64 integer;
f744a9
-} FloatUnion64;
f744a9
-npy_bool   IsNegative_F64(FloatUnion64 *v) { return (v->integer >> 63) != 0; }
f744a9
-npy_uint32 GetExponent_F64(FloatUnion64 *v) { return (v->integer >> 52) & 0x7FF; }
f744a9
-npy_uint64 GetMantissa_F64(FloatUnion64 *v) { return v->integer & 0xFFFFFFFFFFFFFull; }
f744a9
-
f744a9
-/*
f744a9
- * Helper unions and datatype to decompose a 80-bit IEEE float
f744a9
- * sign:      1 bit,  second u64
f744a9
- * exponent: 15 bits, second u64
f744a9
- * intbit     1 bit,  first u64
f744a9
- * mantissa: 63 bits, first u64
f744a9
- */
f744a9
-
f744a9
-/*
f744a9
- * Since systems have different types of long doubles, and may not necessarily
f744a9
- * have a 128-byte format we can use to pass values around, here we create
f744a9
- * our own 128-bit storage type for convenience.
f744a9
- */
f744a9
-typedef struct FloatVal128 {
f744a9
-    npy_uint64 integer[2];
f744a9
-} FloatVal128;
f744a9
-npy_bool   IsNegative_F128(FloatVal128 *v) {
f744a9
-    return ((v->integer[1] >> 15) & 0x1) != 0;
f744a9
-}
f744a9
-npy_uint32 GetExponent_F128(FloatVal128 *v) { return v->integer[1] & 0x7FFF; }
f744a9
-npy_uint64 GetMantissa_F128(FloatVal128 *v) {
f744a9
-    return v->integer[0] &  0x7FFFFFFFFFFFFFFFull;
f744a9
-}
f744a9
-
f744a9
-/*
f744a9
- * then for each different definition of long double, we create a union to
f744a9
- * unpack the float data safely. We can then copy these integers to a
f744a9
- * FloatVal128.
f744a9
+ * The FormatPositional and FormatScientific functions have been more
f744a9
+ * significantly rewritten relative to Ryan Juckett's code.
f744a9
+ *
f744a9
+ * The binary16 and the various 128-bit float functions are new, and adapted
f744a9
+ * from the 64 bit version. The python interface functions are new.
f744a9
  */
f744a9
-#ifdef NPY_FLOAT128
f744a9
-typedef union FloatUnion128
f744a9
-{
f744a9
-    npy_float128 floatingPoint;
f744a9
-    struct {
f744a9
-        npy_uint64 a;
f744a9
-        npy_uint16 b;
f744a9
-    } integer;
f744a9
-} FloatUnion128;
f744a9
-#endif
f744a9
-
f744a9
-#ifdef NPY_FLOAT96
f744a9
-typedef union FloatUnion96
f744a9
-{
f744a9
-    npy_float96 floatingPoint;
f744a9
-    struct {
f744a9
-        npy_uint64 a;
f744a9
-        npy_uint32 b;
f744a9
-    } integer;
f744a9
-} FloatUnion96;
f744a9
-#endif
f744a9
-
f744a9
-#ifdef NPY_FLOAT80
f744a9
-typedef union FloatUnion80
f744a9
-{
f744a9
-    npy_float80 floatingPoint;
f744a9
-    struct {
f744a9
-        npy_uint64 a;
f744a9
-        npy_uint16 b;
f744a9
-    } integer;
f744a9
-} FloatUnion80;
f744a9
-#endif
f744a9
 
f744a9
 
f744a9
-/*
f744a9
- * The main changes above this point, relative to Ryan Juckett's code, are:
f744a9
- *  1. fixed overflow problems when mantissa was 64 bits (in float128 types),
f744a9
- *     by replacing multiplication by 2 or 4 by BigInt_ShiftLeft calls.
f744a9
- *  2. Increased c_BigInt_MaxBlocks
f744a9
- *  3. Added more entries to the g_PowerOf10_Big table
f744a9
- *  4. Added unbiased rounding calculation with isEven
f744a9
+/* Options struct for easy passing of Dragon4 options.
f744a9
  *
f744a9
- * Below this point, the FormatPositional and FormatScientific functions have
f744a9
- * been more significantly rewritten. The Dragon4_PrintFloat16 and
f744a9
- * Dragon4_PrintFloat128 functions are new, and were adapted from the 64 and 32
f744a9
- * bit versions. The python interfacing functions (in the header) are new.
f744a9
+ *   scientific - boolean controlling whether scientific notation is used
f744a9
+ *   digit_mode - whether to use unique or fixed fracional output
f744a9
+ *   cutoff_mode - whether 'precision' refers to toal digits, or digits past
f744a9
+ *                 the decimal point.
f744a9
+ *   precision - When negative, prints as many digits as needed for a unique
f744a9
+ *               number. When positive specifies the maximum number of
f744a9
+ *               significant digits to print.
f744a9
+ *   sign - whether to always show sign
f744a9
+ *   trim_mode - how to treat trailing 0s and '.'. See TrimMode comments.
f744a9
+ *   digits_left - pad characters to left of decimal point. -1 for no padding
f744a9
+ *   digits_right - pad characters to right of decimal point. -1 for no padding.
f744a9
+ *                  Padding adds whitespace until there are the specified
f744a9
+ *                  number characters to sides of decimal point. Applies after
f744a9
+ *                  trim_mode characters were removed. If digits_right is
f744a9
+ *                  positive and the decimal point was trimmed, decimal point
f744a9
+ *                  will be replaced by a whitespace character.
f744a9
+ *   exp_digits - Only affects scientific output. If positive, pads the
f744a9
+ *                exponent with 0s until there are this many digits. If
f744a9
+ *                negative, only use sufficient digits.
f744a9
  */
f744a9
-
f744a9
+typedef struct Dragon4_Options {
f744a9
+    npy_bool scientific;
f744a9
+    DigitMode digit_mode;
f744a9
+    CutoffMode cutoff_mode;
f744a9
+    npy_int32 precision;
f744a9
+    npy_bool sign;
f744a9
+    TrimMode trim_mode;
f744a9
+    npy_int32 digits_left;
f744a9
+    npy_int32 digits_right;
f744a9
+    npy_int32 exp_digits;
f744a9
+} Dragon4_Options;
f744a9
 
f744a9
 /*
f744a9
  * Outputs the positive number with positional notation: ddddd.dddd
f744a9
  * The output is always NUL terminated and the output length (not including the
f744a9
  * NUL) is returned.
f744a9
+ *
f744a9
  * Arguments:
f744a9
  *    buffer - buffer to output into
f744a9
  *    bufferSize - maximum characters that can be printed to buffer
f744a9
@@ -1567,20 +1606,11 @@ typedef union FloatUnion80
f744a9
  *    signbit - value of the sign position. Should be '+', '-' or ''
f744a9
  *    mantissaBit - index of the highest set mantissa bit
f744a9
  *    hasUnequalMargins - is the high margin twice as large as the low margin
f744a9
- *    precision - Negative prints as many digits as are needed for a unique
f744a9
- *                number. Positive specifies the maximum number of significant
f744a9
- *                digits to print past the decimal point.
f744a9
- *    trim_mode - how to treat trailing 0s and '.'. See TrimMode comments.
f744a9
- *    digits_left - pad characters to left of decimal point. -1 for no padding
f744a9
- *    digits_right - pad characters to right of decimal point. -1 for no padding
f744a9
- *                   padding adds whitespace until there are the specified
f744a9
- *                   number characters to sides of decimal point. Applies after
f744a9
- *                   trim_mode characters were removed. If digits_right is
f744a9
- *                   positive and the decimal point was trimmed, decimal point
f744a9
- *                   will be replaced by a whitespace character.
f744a9
+ *
f744a9
+ * See Dragon4_Options for description of remaining arguments.
f744a9
  */
f744a9
 static npy_uint32
f744a9
-FormatPositional(char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa,
f744a9
+FormatPositional(char *buffer, npy_uint32 bufferSize, BigInt *mantissa,
f744a9
                  npy_int32 exponent, char signbit, npy_uint32 mantissaBit,
f744a9
                  npy_bool hasUnequalMargins, DigitMode digit_mode,
f744a9
                  CutoffMode cutoff_mode, npy_int32 precision,
f744a9
@@ -1707,7 +1737,7 @@ FormatPositional(char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa,
f744a9
 
f744a9
     /* always add decimal point, except for DprZeros mode */
f744a9
     if (trim_mode != TrimMode_DptZeros && numFractionDigits == 0 &&
f744a9
-            pos < maxPrintLen){
f744a9
+            pos < maxPrintLen) {
f744a9
         buffer[pos++] = '.';
f744a9
     }
f744a9
 
f744a9
@@ -1745,7 +1775,7 @@ FormatPositional(char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa,
f744a9
      * when rounding, we may still end up with trailing zeros. Remove them
f744a9
      * depending on trim settings.
f744a9
      */
f744a9
-    if (precision >= 0 && trim_mode != TrimMode_None && numFractionDigits > 0){
f744a9
+    if (precision >= 0 && trim_mode != TrimMode_None && numFractionDigits > 0) {
f744a9
         while (buffer[pos-1] == '0') {
f744a9
             pos--;
f744a9
             numFractionDigits--;
f744a9
@@ -1779,7 +1809,7 @@ FormatPositional(char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa,
f744a9
         npy_int32 shift = digits_left - (numWholeDigits + has_sign);
f744a9
         npy_int32 count = pos;
f744a9
 
f744a9
-        if (count + shift > maxPrintLen){
f744a9
+        if (count + shift > maxPrintLen) {
f744a9
             count = maxPrintLen - shift;
f744a9
         }
f744a9
 
f744a9
@@ -1803,6 +1833,7 @@ FormatPositional(char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa,
f744a9
  * Outputs the positive number with scientific notation: d.dddde[sign]ddd
f744a9
  * The output is always NUL terminated and the output length (not including the
f744a9
  * NUL) is returned.
f744a9
+ *
f744a9
  * Arguments:
f744a9
  *    buffer - buffer to output into
f744a9
  *    bufferSize - maximum characters that can be printed to buffer
f744a9
@@ -1811,15 +1842,11 @@ FormatPositional(char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa,
f744a9
  *    signbit - value of the sign position. Should be '+', '-' or ''
f744a9
  *    mantissaBit - index of the highest set mantissa bit
f744a9
  *    hasUnequalMargins - is the high margin twice as large as the low margin
f744a9
- *    precision - Negative prints as many digits as are needed for a unique
f744a9
- *                number. Positive specifies the maximum number of significant
f744a9
- *                digits to print past the decimal point.
f744a9
- *    trim_mode - how to treat trailing 0s and '.'. See TrimMode comments.
f744a9
- *    digits_left - pad characters to left of decimal point. -1 for no padding
f744a9
- *    exp_digits - pads exponent with zeros until it has this many digits
f744a9
+ *
f744a9
+ * See Dragon4_Options for description of remaining arguments.
f744a9
  */
f744a9
 static npy_uint32
f744a9
-FormatScientific (char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa,
f744a9
+FormatScientific (char *buffer, npy_uint32 bufferSize, BigInt *mantissa,
f744a9
                   npy_int32 exponent, char signbit, npy_uint32 mantissaBit,
f744a9
                   npy_bool hasUnequalMargins, DigitMode digit_mode,
f744a9
                   npy_int32 precision, TrimMode trim_mode,
f744a9
@@ -1844,7 +1871,7 @@ FormatScientific (char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa,
f744a9
     leftchars = 1 + (signbit == '-' || signbit == '+');
f744a9
     if (digits_left > leftchars) {
f744a9
         int i;
f744a9
-        for (i = 0; i < digits_left - leftchars && bufferSize > 1; i++){
f744a9
+        for (i = 0; i < digits_left - leftchars && bufferSize > 1; i++) {
f744a9
             *pCurOut = ' ';
f744a9
             pCurOut++;
f744a9
             --bufferSize;
f744a9
@@ -1892,7 +1919,7 @@ FormatScientific (char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa,
f744a9
 
f744a9
     /* always add decimal point, except for DprZeros mode */
f744a9
     if (trim_mode != TrimMode_DptZeros && numFractionDigits == 0 &&
f744a9
-            bufferSize > 1){
f744a9
+            bufferSize > 1) {
f744a9
         *pCurOut = '.';
f744a9
         ++pCurOut;
f744a9
         --bufferSize;
f744a9
@@ -1931,7 +1958,7 @@ FormatScientific (char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa,
f744a9
      * when rounding, we may still end up with trailing zeros. Remove them
f744a9
      * depending on trim settings.
f744a9
      */
f744a9
-    if (precision >= 0 && trim_mode != TrimMode_None && numFractionDigits > 0){
f744a9
+    if (precision >= 0 && trim_mode != TrimMode_None && numFractionDigits > 0) {
f744a9
         --pCurOut;
f744a9
         while (*pCurOut == '0') {
f744a9
             --pCurOut;
f744a9
@@ -1972,7 +1999,7 @@ FormatScientific (char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa,
f744a9
         DEBUG_ASSERT(printExponent < 100000);
f744a9
 
f744a9
         /* get exp digits */
f744a9
-        for (i = 0; i < 5; i++){
f744a9
+        for (i = 0; i < 5; i++) {
f744a9
             digits[i] = printExponent % 10;
f744a9
             printExponent /= 10;
f744a9
         }
f744a9
@@ -1981,7 +2008,7 @@ FormatScientific (char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa,
f744a9
         }
f744a9
         exp_size = i;
f744a9
         /* write remaining digits to tmp buf */
f744a9
-        for (i = exp_size; i > 0; i--){
f744a9
+        for (i = exp_size; i > 0; i--) {
f744a9
             exponentBuffer[2 + (exp_size-i)] = (char)('0' + digits[i-1]);
f744a9
         }
f744a9
 
f744a9
@@ -2057,12 +2084,12 @@ PrintInfNan(char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa,
f744a9
 
f744a9
         /* only print sign for inf values (though nan can have a sign set) */
f744a9
         if (signbit == '+') {
f744a9
-            if (pos < maxPrintLen-1){
f744a9
+            if (pos < maxPrintLen-1) {
f744a9
                 buffer[pos++] = '+';
f744a9
             }
f744a9
         }
f744a9
         else if (signbit == '-') {
f744a9
-            if (pos < maxPrintLen-1){
f744a9
+            if (pos < maxPrintLen-1) {
f744a9
                 buffer[pos++] = '-';
f744a9
             }
f744a9
         }
f744a9
@@ -2080,7 +2107,9 @@ PrintInfNan(char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa,
f744a9
         buffer[pos + printLen] = '\0';
f744a9
 
f744a9
         /*
f744a9
-         * // XXX: Should we change this for numpy?
f744a9
+         *  For numpy we ignore unusual mantissa values for nan, but keep this
f744a9
+         *  code in case we change our mind later.
f744a9
+         *
f744a9
          * // append HEX value
f744a9
          * if (maxPrintLen > 3) {
f744a9
          *     printLen += PrintHex(buffer+3, bufferSize-3, mantissa,
f744a9
@@ -2093,34 +2122,63 @@ PrintInfNan(char *buffer, npy_uint32 bufferSize, npy_uint64 mantissa,
f744a9
 }
f744a9
 
f744a9
 /*
f744a9
- * These functions print a floating-point number as a decimal string.
f744a9
- * The output string is always NUL terminated and the string length (not
f744a9
- * including the NUL) is returned.
f744a9
+ * The functions below format a floating-point numbers stored in particular
f744a9
+ * formats,  as a decimal string.  The output string is always NUL terminated
f744a9
+ * and the string length (not including the NUL) is returned.
f744a9
+ *
f744a9
+ * For 16, 32 and 64 bit floats we assume they are the IEEE 754 type.
f744a9
+ * For 128 bit floats we account for different definitions.
f744a9
  *
f744a9
  * Arguments are:
f744a9
  *   buffer - buffer to output into
f744a9
  *   bufferSize - maximum characters that can be printed to buffer
f744a9
- *   value - value significand
f744a9
- *   scientific - boolean controlling whether scientific notation is used
f744a9
- *   precision - If positive, specifies the number of decimals to show after
f744a9
- *               decimal point. If negative, sufficient digits to uniquely
f744a9
- *               specify the float will be output.
f744a9
- *   trim_mode - how to treat trailing zeros and decimal point. See TrimMode.
f744a9
- *   digits_right - pad the result with '' on the right past the decimal point
f744a9
- *   digits_left - pad the result with '' on the right past the decimal point
f744a9
- *   exp_digits - Only affects scientific output. If positive, pads the
f744a9
- *                exponent with 0s until there are this many digits. If
f744a9
- *                negative, only use sufficient digits.
f744a9
+ *   value - value to print
f744a9
+ *   opt - Dragon4 options, see above
f744a9
+ */
f744a9
+
f744a9
+/*
f744a9
+ * Helper function that takes Dragon4 parameters and options and
f744a9
+ * calls Dragon4.
f744a9
  */
f744a9
 static npy_uint32
f744a9
-Dragon4_PrintFloat16(char *buffer, npy_uint32 bufferSize, npy_uint16 value,
f744a9
-                     npy_bool scientific, DigitMode digit_mode,
f744a9
-                     CutoffMode cutoff_mode, npy_int32 precision,
f744a9
-                     npy_bool sign, TrimMode trim_mode, npy_int32 digits_left,
f744a9
-                     npy_int32 digits_right, npy_int32 exp_digits)
f744a9
+Format_floatbits(char *buffer, npy_uint32 bufferSize, BigInt *mantissa,
f744a9
+                 npy_int32 exponent, char signbit, npy_uint32 mantissaBit,
f744a9
+                 npy_bool hasUnequalMargins, Dragon4_Options *opt)
f744a9
 {
f744a9
-    FloatUnion16 floatUnion;
f744a9
-    npy_uint32 floatExponent, floatMantissa;
f744a9
+    /* format the value */
f744a9
+    if (opt->scientific) {
f744a9
+        return FormatScientific(buffer, bufferSize, mantissa, exponent,
f744a9
+                                signbit, mantissaBit, hasUnequalMargins,
f744a9
+                                opt->digit_mode, opt->precision,
f744a9
+                                opt->trim_mode, opt->digits_left,
f744a9
+                                opt->exp_digits);
f744a9
+    }
f744a9
+    else {
f744a9
+        return FormatPositional(buffer, bufferSize, mantissa, exponent,
f744a9
+                                signbit, mantissaBit, hasUnequalMargins,
f744a9
+                                opt->digit_mode, opt->cutoff_mode,
f744a9
+                                opt->precision, opt->trim_mode,
f744a9
+                                opt->digits_left, opt->digits_right);
f744a9
+    }
f744a9
+}
f744a9
+
f744a9
+/*
f744a9
+ * IEEE binary16 floating-point format
f744a9
+ *
f744a9
+ * sign:      1 bit
f744a9
+ * exponent:  5 bits
f744a9
+ * mantissa: 10 bits
f744a9
+ */
f744a9
+static npy_uint32
f744a9
+Dragon4_PrintFloat_IEEE_binary16(
f744a9
+        Dragon4_Scratch *scratch, npy_half *value, Dragon4_Options *opt)
f744a9
+{
f744a9
+    char *buffer = scratch->repr;
f744a9
+    npy_uint32 bufferSize = sizeof(scratch->repr);
f744a9
+    BigInt *bigints = scratch->bigints;
f744a9
+
f744a9
+    npy_uint16 val = *value;
f744a9
+    npy_uint32 floatExponent, floatMantissa, floatSign;
f744a9
 
f744a9
     npy_uint32 mantissa;
f744a9
     npy_int32 exponent;
f744a9
@@ -2138,20 +2196,20 @@ Dragon4_PrintFloat16(char *buffer, npy_uint32 bufferSize, npy_uint16 value,
f744a9
     }
f744a9
 
f744a9
     /* deconstruct the floating point value */
f744a9
-    floatUnion.integer = value;
f744a9
-    floatExponent = GetExponent_F16(&floatUnion);
f744a9
-    floatMantissa = GetMantissa_F16(&floatUnion);
f744a9
+    floatMantissa = val & bitmask_u32(10);
f744a9
+    floatExponent = (val >> 10) & bitmask_u32(5);
f744a9
+    floatSign = val >> 15;
f744a9
 
f744a9
     /* output the sign */
f744a9
-    if (IsNegative_F16(&floatUnion)) {
f744a9
+    if (floatSign != 0) {
f744a9
         signbit = '-';
f744a9
     }
f744a9
-    else if (sign) {
f744a9
+    else if (opt->sign) {
f744a9
         signbit = '+';
f744a9
     }
f744a9
 
f744a9
     /* if this is a special value */
f744a9
-    if (floatExponent == 0x1F) {
f744a9
+    if (floatExponent == bitmask_u32(5)) {
f744a9
         return PrintInfNan(buffer, bufferSize, floatMantissa, 3, signbit);
f744a9
     }
f744a9
     /* else this is a number */
f744a9
@@ -2195,29 +2253,33 @@ Dragon4_PrintFloat16(char *buffer, npy_uint32 bufferSize, npy_uint16 value,
f744a9
         hasUnequalMargins  = NPY_FALSE;
f744a9
     }
f744a9
 
f744a9
-    /* format the value */
f744a9
-    if (scientific) {
f744a9
-        return FormatScientific(buffer, bufferSize, mantissa, exponent, signbit,
f744a9
-                                mantissaBit, hasUnequalMargins, digit_mode,
f744a9
-                                precision, trim_mode, digits_left, exp_digits);
f744a9
-    }
f744a9
-    else {
f744a9
-        return FormatPositional(buffer, bufferSize, mantissa, exponent, signbit,
f744a9
-                                mantissaBit, hasUnequalMargins, digit_mode,
f744a9
-                                cutoff_mode, precision, trim_mode,
f744a9
-                                digits_left, digits_right);
f744a9
-    }
f744a9
+    BigInt_Set_uint32(&bigints[0], mantissa);
f744a9
+    return Format_floatbits(buffer, bufferSize, bigints, exponent,
f744a9
+                            signbit, mantissaBit, hasUnequalMargins, opt);
f744a9
 }
f744a9
 
f744a9
+/*
f744a9
+ * IEEE binary32 floating-point format
f744a9
+ *
f744a9
+ * sign:      1 bit
f744a9
+ * exponent:  8 bits
f744a9
+ * mantissa: 23 bits
f744a9
+ */
f744a9
 static npy_uint32
f744a9
-Dragon4_PrintFloat32(char *buffer, npy_uint32 bufferSize, npy_float32 value,
f744a9
-                     npy_bool scientific, DigitMode digit_mode,
f744a9
-                     CutoffMode cutoff_mode, npy_int32 precision,
f744a9
-                     npy_bool sign, TrimMode trim_mode, npy_int32 digits_left,
f744a9
-                     npy_int32 digits_right, npy_int32 exp_digits)
f744a9
+Dragon4_PrintFloat_IEEE_binary32(
f744a9
+        Dragon4_Scratch *scratch, npy_float32 *value,
f744a9
+        Dragon4_Options *opt)
f744a9
 {
f744a9
-    FloatUnion32 floatUnion;
f744a9
-    npy_uint32 floatExponent, floatMantissa;
f744a9
+    char *buffer = scratch->repr;
f744a9
+    npy_uint32 bufferSize = sizeof(scratch->repr);
f744a9
+    BigInt *bigints = scratch->bigints;
f744a9
+
f744a9
+    union
f744a9
+    {
f744a9
+        npy_float32 floatingPoint;
f744a9
+        npy_uint32 integer;
f744a9
+    } floatUnion;
f744a9
+    npy_uint32 floatExponent, floatMantissa, floatSign;
f744a9
 
f744a9
     npy_uint32 mantissa;
f744a9
     npy_int32 exponent;
f744a9
@@ -2235,20 +2297,21 @@ Dragon4_PrintFloat32(char *buffer, npy_uint32 bufferSize, npy_float32 value,
f744a9
     }
f744a9
 
f744a9
     /* deconstruct the floating point value */
f744a9
-    floatUnion.floatingPoint = value;
f744a9
-    floatExponent = GetExponent_F32(&floatUnion);
f744a9
-    floatMantissa = GetMantissa_F32(&floatUnion);
f744a9
+    floatUnion.floatingPoint = *value;
f744a9
+    floatMantissa = floatUnion.integer & bitmask_u32(23);
f744a9
+    floatExponent = (floatUnion.integer >> 23) & bitmask_u32(8);
f744a9
+    floatSign = floatUnion.integer >> 31;
f744a9
 
f744a9
     /* output the sign */
f744a9
-    if (IsNegative_F32(&floatUnion)) {
f744a9
+    if (floatSign != 0) {
f744a9
         signbit = '-';
f744a9
     }
f744a9
-    else if (sign) {
f744a9
+    else if (opt->sign) {
f744a9
         signbit = '+';
f744a9
     }
f744a9
 
f744a9
     /* if this is a special value */
f744a9
-    if (floatExponent == 0xFF) {
f744a9
+    if (floatExponent == bitmask_u32(8)) {
f744a9
         return PrintInfNan(buffer, bufferSize, floatMantissa, 6, signbit);
f744a9
     }
f744a9
     /* else this is a number */
f744a9
@@ -2292,29 +2355,32 @@ Dragon4_PrintFloat32(char *buffer, npy_uint32 bufferSize, npy_float32 value,
f744a9
         hasUnequalMargins  = NPY_FALSE;
f744a9
     }
f744a9
 
f744a9
-    /* format the value */
f744a9
-    if (scientific) {
f744a9
-        return FormatScientific(buffer, bufferSize, mantissa, exponent, signbit,
f744a9
-                                mantissaBit, hasUnequalMargins, digit_mode,
f744a9
-                                precision, trim_mode, digits_left, exp_digits);
f744a9
-    }
f744a9
-    else {
f744a9
-        return FormatPositional(buffer, bufferSize, mantissa, exponent, signbit,
f744a9
-                                mantissaBit, hasUnequalMargins, digit_mode,
f744a9
-                                cutoff_mode, precision, trim_mode,
f744a9
-                                digits_left, digits_right);
f744a9
-    }
f744a9
+    BigInt_Set_uint32(&bigints[0], mantissa);
f744a9
+    return Format_floatbits(buffer, bufferSize, bigints, exponent,
f744a9
+                           signbit, mantissaBit, hasUnequalMargins, opt);
f744a9
 }
f744a9
 
f744a9
+/*
f744a9
+ * IEEE binary64 floating-point format
f744a9
+ *
f744a9
+ * sign:      1 bit
f744a9
+ * exponent: 11 bits
f744a9
+ * mantissa: 52 bits
f744a9
+ */
f744a9
 static npy_uint32
f744a9
-Dragon4_PrintFloat64(char *buffer, npy_uint32 bufferSize, npy_float64 value,
f744a9
-                     npy_bool scientific, DigitMode digit_mode,
f744a9
-                     CutoffMode cutoff_mode, npy_int32 precision,
f744a9
-                     npy_bool sign, TrimMode trim_mode, npy_int32 digits_left,
f744a9
-                     npy_int32 digits_right, npy_int32 exp_digits)
f744a9
+Dragon4_PrintFloat_IEEE_binary64(
f744a9
+        Dragon4_Scratch *scratch, npy_float64 *value, Dragon4_Options *opt)
f744a9
 {
f744a9
-    FloatUnion64 floatUnion;
f744a9
-    npy_uint32 floatExponent;
f744a9
+    char *buffer = scratch->repr;
f744a9
+    npy_uint32 bufferSize = sizeof(scratch->repr);
f744a9
+    BigInt *bigints = scratch->bigints;
f744a9
+
f744a9
+    union
f744a9
+    {
f744a9
+        npy_float64 floatingPoint;
f744a9
+        npy_uint64 integer;
f744a9
+    } floatUnion;
f744a9
+    npy_uint32 floatExponent, floatSign;
f744a9
     npy_uint64 floatMantissa;
f744a9
 
f744a9
     npy_uint64 mantissa;
f744a9
@@ -2333,20 +2399,21 @@ Dragon4_PrintFloat64(char *buffer, npy_uint32 bufferSize, npy_float64 value,
f744a9
     }
f744a9
 
f744a9
     /* deconstruct the floating point value */
f744a9
-    floatUnion.floatingPoint = value;
f744a9
-    floatExponent = GetExponent_F64(&floatUnion);
f744a9
-    floatMantissa = GetMantissa_F64(&floatUnion);
f744a9
+    floatUnion.floatingPoint = *value;
f744a9
+    floatMantissa = floatUnion.integer & bitmask_u64(52);
f744a9
+    floatExponent = (floatUnion.integer >> 52) & bitmask_u32(11);
f744a9
+    floatSign = floatUnion.integer >> 63;
f744a9
 
f744a9
     /* output the sign */
f744a9
-    if (IsNegative_F64(&floatUnion)) {
f744a9
+    if (floatSign != 0) {
f744a9
         signbit = '-';
f744a9
     }
f744a9
-    else if (sign) {
f744a9
+    else if (opt->sign) {
f744a9
         signbit = '+';
f744a9
     }
f744a9
 
f744a9
     /* if this is a special value */
f744a9
-    if (floatExponent == 0x7FF) {
f744a9
+    if (floatExponent == bitmask_u32(11)) {
f744a9
         return PrintInfNan(buffer, bufferSize, floatMantissa, 13, signbit);
f744a9
     }
f744a9
     /* else this is a number */
f744a9
@@ -2390,28 +2457,48 @@ Dragon4_PrintFloat64(char *buffer, npy_uint32 bufferSize, npy_float64 value,
f744a9
         hasUnequalMargins   = NPY_FALSE;
f744a9
     }
f744a9
 
f744a9
-    /* format the value */
f744a9
-    if (scientific) {
f744a9
-        return FormatScientific(buffer, bufferSize, mantissa, exponent, signbit,
f744a9
-                                mantissaBit, hasUnequalMargins, digit_mode,
f744a9
-                                precision, trim_mode, digits_left, exp_digits);
f744a9
-    }
f744a9
-    else {
f744a9
-        return FormatPositional(buffer, bufferSize, mantissa, exponent, signbit,
f744a9
-                                mantissaBit, hasUnequalMargins, digit_mode,
f744a9
-                                cutoff_mode, precision, trim_mode,
f744a9
-                                digits_left, digits_right);
f744a9
-    }
f744a9
+    BigInt_Set_uint64(&bigints[0], mantissa);
f744a9
+    return Format_floatbits(buffer, bufferSize, bigints, exponent,
f744a9
+                            signbit, mantissaBit, hasUnequalMargins, opt);
f744a9
 }
f744a9
 
f744a9
+
f744a9
+/*
f744a9
+ * Since systems have different types of long doubles, and may not necessarily
f744a9
+ * have a 128-byte format we can use to pass values around, here we create
f744a9
+ * our own 128-bit storage type for convenience.
f744a9
+ */
f744a9
+typedef struct FloatVal128 {
f744a9
+    npy_uint64 hi, lo;
f744a9
+} FloatVal128;
f744a9
+
f744a9
+#if defined(HAVE_LDOUBLE_INTEL_EXTENDED_10_BYTES_LE) || \
f744a9
+    defined(HAVE_LDOUBLE_INTEL_EXTENDED_12_BYTES_LE) || \
f744a9
+    defined(HAVE_LDOUBLE_INTEL_EXTENDED_16_BYTES_LE) || \
f744a9
+    defined(HAVE_LDOUBLE_MOTOROLA_EXTENDED_12_BYTES_BE)
f744a9
+/*
f744a9
+ * Intel's 80-bit IEEE extended precision floating-point format
f744a9
+ *
f744a9
+ * "long doubles" with this format are stored as 96 or 128 bits, but
f744a9
+ * are equivalent to the 80 bit type with some zero padding on the high bits.
f744a9
+ * This method expects the user to pass in the value using a 128-bit
f744a9
+ * FloatVal128, so can support 80, 96, or 128 bit storage formats,
f744a9
+ * and is endian-independent.
f744a9
+ *
f744a9
+ * sign:      1 bit,  second u64
f744a9
+ * exponent: 15 bits, second u64
f744a9
+ * intbit     1 bit,  first u64
f744a9
+ * mantissa: 63 bits, first u64
f744a9
+ */
f744a9
 static npy_uint32
f744a9
-Dragon4_PrintFloat128(char *buffer, npy_uint32 bufferSize, FloatVal128 value,
f744a9
-                      npy_bool scientific, DigitMode digit_mode,
f744a9
-                      CutoffMode cutoff_mode, npy_int32 precision,
f744a9
-                      npy_bool sign, TrimMode trim_mode, npy_int32 digits_left,
f744a9
-                      npy_int32 digits_right, npy_int32 exp_digits)
f744a9
+Dragon4_PrintFloat_Intel_extended(
f744a9
+    Dragon4_Scratch *scratch, FloatVal128 value, Dragon4_Options *opt)
f744a9
 {
f744a9
-    npy_uint32 floatExponent;
f744a9
+    char *buffer = scratch->repr;
f744a9
+    npy_uint32 bufferSize = sizeof(scratch->repr);
f744a9
+    BigInt *bigints = scratch->bigints;
f744a9
+
f744a9
+    npy_uint32 floatExponent, floatSign;
f744a9
     npy_uint64 floatMantissa;
f744a9
 
f744a9
     npy_uint64 mantissa;
f744a9
@@ -2429,20 +2516,27 @@ Dragon4_PrintFloat128(char *buffer, npy_uint32 bufferSize, FloatVal128 value,
f744a9
         return 0;
f744a9
     }
f744a9
 
f744a9
-    /* deconstruct the floating point value */
f744a9
-    floatExponent = GetExponent_F128(&value);
f744a9
-    floatMantissa = GetMantissa_F128(&value);
f744a9
+    /* deconstruct the floating point value (we ignore the intbit) */
f744a9
+    floatMantissa = value.lo & bitmask_u64(63);
f744a9
+    floatExponent = value.hi & bitmask_u32(15);
f744a9
+    floatSign = (value.hi >> 15) & 0x1;
f744a9
 
f744a9
     /* output the sign */
f744a9
-    if (IsNegative_F128(&value)) {
f744a9
+    if (floatSign != 0) {
f744a9
         signbit = '-';
f744a9
     }
f744a9
-    else if (sign) {
f744a9
+    else if (opt->sign) {
f744a9
         signbit = '+';
f744a9
     }
f744a9
 
f744a9
     /* if this is a special value */
f744a9
-    if (floatExponent == 0x7FFF) {
f744a9
+    if (floatExponent == bitmask_u32(15)) {
f744a9
+        /*
f744a9
+         * Note: Technically there are other special extended values defined if
f744a9
+         * the intbit is 0, like Pseudo-Infinity, Pseudo-Nan, Quiet-NaN. We
f744a9
+         * ignore all of these since they are not generated on modern
f744a9
+         * processors. We treat Quiet-Nan as simply Nan.
f744a9
+         */
f744a9
         return PrintInfNan(buffer, bufferSize, floatMantissa, 16, signbit);
f744a9
     }
f744a9
     /* else this is a number */
f744a9
@@ -2486,247 +2580,668 @@ Dragon4_PrintFloat128(char *buffer, npy_uint32 bufferSize, FloatVal128 value,
f744a9
         hasUnequalMargins   = NPY_FALSE;
f744a9
     }
f744a9
 
f744a9
-    /* format the value */
f744a9
-    if (scientific) {
f744a9
-        return FormatScientific(buffer, bufferSize, mantissa, exponent, signbit,
f744a9
-                                mantissaBit, hasUnequalMargins, digit_mode,
f744a9
-                                precision, trim_mode, digits_left, exp_digits);
f744a9
+    BigInt_Set_uint64(&bigints[0], mantissa);
f744a9
+    return Format_floatbits(buffer, bufferSize, bigints, exponent,
f744a9
+                            signbit, mantissaBit, hasUnequalMargins, opt);
f744a9
+
f744a9
+}
f744a9
+
f744a9
+#endif /* INTEL_EXTENDED group */
f744a9
+
f744a9
+
f744a9
+#ifdef HAVE_LDOUBLE_INTEL_EXTENDED_10_BYTES_LE
f744a9
+/*
f744a9
+ * Intel's 80-bit IEEE extended precision format, 80-bit storage
f744a9
+ *
f744a9
+ * Note: It is not clear if a long double with 10-byte storage exists on any
f744a9
+ * system. But numpy defines NPY_FLOAT80, so if we come across it, assume it is
f744a9
+ * an Intel extended format.
f744a9
+ */
f744a9
+static npy_uint32
f744a9
+Dragon4_PrintFloat_Intel_extended80(
f744a9
+    Dragon4_Scratch *scratch, npy_float80 *value, Dragon4_Options *opt)
f744a9
+{
f744a9
+    FloatVal128 val128;
f744a9
+    union {
f744a9
+        npy_float80 floatingPoint;
f744a9
+        struct {
f744a9
+            npy_uint64 a;
f744a9
+            npy_uint16 b;
f744a9
+        } integer;
f744a9
+    } buf80;
f744a9
+
f744a9
+    buf80.floatingPoint = *value;
f744a9
+    /* Intel is little-endian */
f744a9
+    val128.lo = buf80.integer.a;
f744a9
+    val128.hi = buf80.integer.b;
f744a9
+
f744a9
+    return Dragon4_PrintFloat_Intel_extended(scratch, val128, opt);
f744a9
+}
f744a9
+#endif /* HAVE_LDOUBLE_INTEL_EXTENDED_10_BYTES_LE */
f744a9
+
f744a9
+#ifdef HAVE_LDOUBLE_INTEL_EXTENDED_12_BYTES_LE
f744a9
+/* Intel's 80-bit IEEE extended precision format, 96-bit storage */
f744a9
+static npy_uint32
f744a9
+Dragon4_PrintFloat_Intel_extended96(
f744a9
+    Dragon4_Scratch *scratch, npy_float96 *value, Dragon4_Options *opt)
f744a9
+{
f744a9
+    FloatVal128 val128;
f744a9
+    union {
f744a9
+        npy_float96 floatingPoint;
f744a9
+        struct {
f744a9
+            npy_uint64 a;
f744a9
+            npy_uint32 b;
f744a9
+        } integer;
f744a9
+    } buf96;
f744a9
+
f744a9
+    buf96.floatingPoint = *value;
f744a9
+    /* Intel is little-endian */
f744a9
+    val128.lo = buf96.integer.a;
f744a9
+    val128.hi = buf96.integer.b;
f744a9
+
f744a9
+    return Dragon4_PrintFloat_Intel_extended(scratch, val128, opt);
f744a9
+}
f744a9
+#endif /* HAVE_LDOUBLE_INTEL_EXTENDED_12_BYTES_LE */
f744a9
+
f744a9
+#ifdef HAVE_LDOUBLE_MOTOROLA_EXTENDED_12_BYTES_BE
f744a9
+/* Motorola Big-endian equivalent of the Intel-extended 96 fp format */
f744a9
+static npy_uint32
f744a9
+Dragon4_PrintFloat_Motorola_extended96(
f744a9
+    Dragon4_Scratch *scratch, npy_float96 *value, Dragon4_Options *opt)
f744a9
+{
f744a9
+    FloatVal128 val128;
f744a9
+    union {
f744a9
+        npy_float96 floatingPoint;
f744a9
+        struct {
f744a9
+            npy_uint64 a;
f744a9
+            npy_uint32 b;
f744a9
+        } integer;
f744a9
+    } buf96;
f744a9
+
f744a9
+    buf96.floatingPoint = *value;
f744a9
+    /* Motorola is big-endian */
f744a9
+    val128.lo = buf96.integer.b;
f744a9
+    val128.hi = buf96.integer.a >> 16;
f744a9
+    /* once again we assume the int has same endianness as the float */
f744a9
+
f744a9
+    return Dragon4_PrintFloat_Intel_extended(scratch, val128, opt);
f744a9
+}
f744a9
+#endif /* HAVE_LDOUBLE_MOTOROLA_EXTENDED_12_BYTES_BE */
f744a9
+
f744a9
+
f744a9
+#ifdef NPY_FLOAT128
f744a9
+
f744a9
+typedef union FloatUnion128
f744a9
+{
f744a9
+    npy_float128 floatingPoint;
f744a9
+    struct {
f744a9
+        npy_uint64 a;
f744a9
+        npy_uint64 b;
f744a9
+    } integer;
f744a9
+} FloatUnion128;
f744a9
+
f744a9
+#ifdef HAVE_LDOUBLE_INTEL_EXTENDED_16_BYTES_LE
f744a9
+/* Intel's 80-bit IEEE extended precision format, 128-bit storage */
f744a9
+static npy_uint32
f744a9
+Dragon4_PrintFloat_Intel_extended128(
f744a9
+    Dragon4_Scratch *scratch, npy_float128 *value, Dragon4_Options *opt)
f744a9
+{
f744a9
+    FloatVal128 val128;
f744a9
+    FloatUnion128 buf128;
f744a9
+
f744a9
+    buf128.floatingPoint = *value;
f744a9
+    /* Intel is little-endian */
f744a9
+    val128.lo = buf128.integer.a;
f744a9
+    val128.hi = buf128.integer.b;
f744a9
+
f744a9
+    return Dragon4_PrintFloat_Intel_extended(scratch, val128, opt);
f744a9
+}
f744a9
+#endif /* HAVE_LDOUBLE_INTEL_EXTENDED_16_BYTES_LE */
f744a9
+
f744a9
+#if defined(HAVE_LDOUBLE_IEEE_QUAD_LE) || defined(HAVE_LDOUBLE_IEEE_QUAD_BE)
f744a9
+/*
f744a9
+ * IEEE binary128 floating-point format
f744a9
+ *
f744a9
+ * sign:       1 bit
f744a9
+ * exponent:  15 bits
f744a9
+ * mantissa: 112 bits
f744a9
+ *
f744a9
+ * Currently binary128 format exists on only a few CPUs, such as on the POWER9
f744a9
+ * arch or aarch64. Because of this, this code has not been extensively tested.
f744a9
+ * I am not sure if the arch also supports uint128, and C does not seem to
f744a9
+ * support int128 literals. So we use uint64 to do manipulation.
f744a9
+ */
f744a9
+static npy_uint32
f744a9
+Dragon4_PrintFloat_IEEE_binary128(
f744a9
+    Dragon4_Scratch *scratch, FloatVal128 val128, Dragon4_Options *opt)
f744a9
+{
f744a9
+    char *buffer = scratch->repr;
f744a9
+    npy_uint32 bufferSize = sizeof(scratch->repr);
f744a9
+    BigInt *bigints = scratch->bigints;
f744a9
+
f744a9
+    npy_uint32 floatExponent, floatSign;
f744a9
+
f744a9
+    npy_uint64 mantissa_hi, mantissa_lo;
f744a9
+    npy_int32 exponent;
f744a9
+    npy_uint32 mantissaBit;
f744a9
+    npy_bool hasUnequalMargins;
f744a9
+    char signbit = '\0';
f744a9
+
f744a9
+    if (bufferSize == 0) {
f744a9
+        return 0;
f744a9
+    }
f744a9
+
f744a9
+    if (bufferSize == 1) {
f744a9
+        buffer[0] = '\0';
f744a9
+        return 0;
f744a9
+    }
f744a9
+
f744a9
+    mantissa_hi = val128.hi & bitmask_u64(48);
f744a9
+    mantissa_lo = val128.lo;
f744a9
+    floatExponent = (val128.hi >> 48) & bitmask_u32(15);
f744a9
+    floatSign = val128.hi >> 63;
f744a9
+
f744a9
+    /* output the sign */
f744a9
+    if (floatSign != 0) {
f744a9
+        signbit = '-';
f744a9
+    }
f744a9
+    else if (opt->sign) {
f744a9
+        signbit = '+';
f744a9
+    }
f744a9
+
f744a9
+    /* if this is a special value */
f744a9
+    if (floatExponent == bitmask_u32(15)) {
f744a9
+        npy_uint64 mantissa_zero = mantissa_hi == 0 && mantissa_lo == 0;
f744a9
+        return PrintInfNan(buffer, bufferSize, !mantissa_zero, 16, signbit);
f744a9
+    }
f744a9
+    /* else this is a number */
f744a9
+
f744a9
+    /* factor the value into its parts */
f744a9
+    if (floatExponent != 0) {
f744a9
+        /*
f744a9
+         * normal
f744a9
+         * The floating point equation is:
f744a9
+         *  value = (1 + mantissa/2^112) * 2 ^ (exponent-16383)
f744a9
+         * We convert the integer equation by factoring a 2^112 out of the
f744a9
+         * exponent
f744a9
+         *  value = (1 + mantissa/2^112) * 2^112 * 2 ^ (exponent-16383-112)
f744a9
+         *  value = (2^112 + mantissa) * 2 ^ (exponent-16383-112)
f744a9
+         * Because of the implied 1 in front of the mantissa we have 112 bits of
f744a9
+         * precision.
f744a9
+         *   m = (2^112 + mantissa)
f744a9
+         *   e = (exponent-16383+1-112)
f744a9
+         *
f744a9
+         *   Adding 2^112 to the mantissa is the same as adding 2^48 to the hi
f744a9
+         *   64 bit part.
f744a9
+         */
f744a9
+        mantissa_hi         = (1ull << 48) | mantissa_hi;
f744a9
+        /* mantissa_lo is unchanged */
f744a9
+        exponent            = floatExponent - 16383 - 112;
f744a9
+        mantissaBit         = 112;
f744a9
+        hasUnequalMargins   = (floatExponent != 1) && (mantissa_hi == 0 &&
f744a9
+                                                       mantissa_lo == 0);
f744a9
     }
f744a9
     else {
f744a9
-        return FormatPositional(buffer, bufferSize, mantissa, exponent, signbit,
f744a9
-                                mantissaBit, hasUnequalMargins, digit_mode,
f744a9
-                                cutoff_mode, precision, trim_mode,
f744a9
-                                digits_left, digits_right);
f744a9
+        /*
f744a9
+         * subnormal
f744a9
+         * The floating point equation is:
f744a9
+         *  value = (mantissa/2^112) * 2 ^ (1-16383)
f744a9
+         * We convert the integer equation by factoring a 2^112 out of the
f744a9
+         * exponent
f744a9
+         *  value = (mantissa/2^112) * 2^112 * 2 ^ (1-16383-112)
f744a9
+         *  value = mantissa * 2 ^ (1-16383-112)
f744a9
+         * We have up to 112 bits of precision.
f744a9
+         *   m = (mantissa)
f744a9
+         *   e = (1-16383-112)
f744a9
+         */
f744a9
+        exponent            = 1 - 16383 - 112;
f744a9
+        mantissaBit         = LogBase2_128(mantissa_hi, mantissa_lo);
f744a9
+        hasUnequalMargins   = NPY_FALSE;
f744a9
     }
f744a9
+
f744a9
+    BigInt_Set_2x_uint64(&bigints[0], mantissa_hi, mantissa_lo);
f744a9
+    return Format_floatbits(buffer, bufferSize, bigints, exponent,
f744a9
+                            signbit, mantissaBit, hasUnequalMargins, opt);
f744a9
 }
f744a9
 
f744a9
-PyObject *
f744a9
-Dragon4_Positional_AnySize(void *val, size_t size, DigitMode digit_mode,
f744a9
-                           CutoffMode cutoff_mode, int precision, int sign,
f744a9
-                           TrimMode trim, int pad_left, int pad_right)
f744a9
+#if defined(HAVE_LDOUBLE_IEEE_QUAD_LE)
f744a9
+static npy_uint32
f744a9
+Dragon4_PrintFloat_IEEE_binary128_le(
f744a9
+    Dragon4_Scratch *scratch, npy_float128 *value, Dragon4_Options *opt)
f744a9
 {
f744a9
-    /*
f744a9
-     * Use a very large buffer in case anyone tries to output a large numberG.
f744a9
-     * 16384 should be enough to uniquely print any float128, which goes up
f744a9
-     * to about 10^4932 */
f744a9
-    static char repr[16384];
f744a9
     FloatVal128 val128;
f744a9
-#ifdef NPY_FLOAT80
f744a9
-    FloatUnion80 buf80;;
f744a9
-#endif
f744a9
-#ifdef NPY_FLOAT96
f744a9
-    FloatUnion96 buf96;
f744a9
-#endif
f744a9
-#ifdef NPY_FLOAT128
f744a9
     FloatUnion128 buf128;
f744a9
-#endif
f744a9
 
f744a9
-    switch (size) {
f744a9
-        case 2:
f744a9
-            Dragon4_PrintFloat16(repr, sizeof(repr), *(npy_float16*)val,
f744a9
-                                 0, digit_mode, cutoff_mode, precision,
f744a9
-                                 sign, trim, pad_left, pad_right, -1);
f744a9
-            break;
f744a9
-        case 4:
f744a9
-            Dragon4_PrintFloat32(repr, sizeof(repr), *(npy_float32*)val,
f744a9
-                                 0, digit_mode, cutoff_mode, precision,
f744a9
-                                 sign, trim, pad_left, pad_right, -1);
f744a9
-            break;
f744a9
-        case 8:
f744a9
-            Dragon4_PrintFloat64(repr, sizeof(repr), *(npy_float64*)val,
f744a9
-                                 0, digit_mode, cutoff_mode, precision,
f744a9
-                                 sign, trim, pad_left, pad_right, -1);
f744a9
-            break;
f744a9
-#ifdef NPY_FLOAT80
f744a9
-        case 10:
f744a9
-            buf80.floatingPoint = *(npy_float80*)val;
f744a9
-            val128.integer[0] = buf80.integer.a;
f744a9
-            val128.integer[1] = buf80.integer.b;
f744a9
-            Dragon4_PrintFloat128(repr, sizeof(repr), val128,
f744a9
-                                  0, digit_mode, cutoff_mode, precision,
f744a9
-                                  sign, trim, pad_left, pad_right, -1);
f744a9
-            break;
f744a9
-#endif
f744a9
-#ifdef NPY_FLOAT96
f744a9
-        case 12:
f744a9
-            buf96.floatingPoint = *(npy_float96*)val;
f744a9
-            val128.integer[0] = buf96.integer.a;
f744a9
-            val128.integer[1] = buf96.integer.b;
f744a9
-            Dragon4_PrintFloat128(repr, sizeof(repr), val128,
f744a9
-                                  0, digit_mode, cutoff_mode, precision,
f744a9
-                                  sign, trim, pad_left, pad_right, -1);
f744a9
-            break;
f744a9
-#endif
f744a9
-#ifdef NPY_FLOAT128
f744a9
-        case 16:
f744a9
-            buf128.floatingPoint = *(npy_float128*)val;
f744a9
-            val128.integer[0] = buf128.integer.a;
f744a9
-            val128.integer[1] = buf128.integer.b;
f744a9
-            Dragon4_PrintFloat128(repr, sizeof(repr), val128,
f744a9
-                                  0, digit_mode, cutoff_mode, precision,
f744a9
-                                  sign, trim, pad_left, pad_right, -1);
f744a9
-            break;
f744a9
-#endif
f744a9
-        default:
f744a9
-            PyErr_Format(PyExc_ValueError, "unexpected itemsize %zu", size);
f744a9
-            return NULL;
f744a9
+    buf128.floatingPoint = *value;
f744a9
+    val128.lo = buf128.integer.a;
f744a9
+    val128.hi = buf128.integer.b;
f744a9
+
f744a9
+    return Dragon4_PrintFloat_IEEE_binary128(scratch, val128, opt);
f744a9
+}
f744a9
+#endif /* HAVE_LDOUBLE_IEEE_QUAD_LE */
f744a9
+
f744a9
+#if defined(HAVE_LDOUBLE_IEEE_QUAD_BE)
f744a9
+/*
f744a9
+ * This function is untested, very few, if any, architectures implement
f744a9
+ * big endian IEEE binary128 floating point.
f744a9
+ */
f744a9
+static npy_uint32
f744a9
+Dragon4_PrintFloat_IEEE_binary128_be(
f744a9
+    Dragon4_Scratch *scratch, npy_float128 *value, Dragon4_Options *opt)
f744a9
+{
f744a9
+    FloatVal128 val128;
f744a9
+    FloatUnion128 buf128;
f744a9
+
f744a9
+    buf128.floatingPoint = *value;
f744a9
+    val128.lo = buf128.integer.b;
f744a9
+    val128.hi = buf128.integer.a;
f744a9
+
f744a9
+    return Dragon4_PrintFloat_IEEE_binary128(scratch, val128, opt);
f744a9
+}
f744a9
+#endif /* HAVE_LDOUBLE_IEEE_QUAD_BE */
f744a9
+
f744a9
+#endif /* HAVE_LDOUBLE_IEEE_QUAD_LE | HAVE_LDOUBLE_IEEE_BE*/
f744a9
+
f744a9
+#if (defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_LE) || \
f744a9
+     defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE))
f744a9
+/*
f744a9
+ * IBM extended precision 128-bit floating-point format, aka IBM double-double
f744a9
+ *
f744a9
+ * IBM's double-double type is a pair of IEEE binary64 values, which you add
f744a9
+ * together to get a total value. The exponents are arranged so that the lower
f744a9
+ * double is about 2^52 times smaller than the high one, and the nearest
f744a9
+ * float64 value is simply the upper double, in which case the pair is
f744a9
+ * considered "normalized" (not to confuse with "normal" and "subnormal"
f744a9
+ * binary64 values). We assume normalized values. You can see the glibc's
f744a9
+ * printf on ppc does so too by constructing un-normalized values to get
f744a9
+ * strange behavior from the OS printf:
f744a9
+ *
f744a9
+ *     >>> from numpy.core._multiarray_tests import format_float_OSprintf_g
f744a9
+ *     >>> x = np.array([0.3,0.3], dtype='f8').view('f16')[0]
f744a9
+ *     >>> format_float_OSprintf_g(x, 2)
f744a9
+ *     0.30
f744a9
+ *     >>> format_float_OSprintf_g(2*x, 2)
f744a9
+ *     1.20
f744a9
+ *
f744a9
+ * If we don't assume normalization, x should really print as 0.6.
f744a9
+ *
f744a9
+ * For normalized values gcc assumes that the total mantissa is no
f744a9
+ * more than 106 bits (53+53), so we can drop bits from the second double which
f744a9
+ * would be pushed past 106 when left-shifting by its exponent, as happens
f744a9
+ * sometimes. (There has been debate about this, see
f744a9
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?format=multiple&id=70117,
f744a9
+ * https://sourceware.org/bugzilla/show_bug.cgi?id=22752 )
f744a9
+ *
f744a9
+ * Note: This function is for the IBM-double-double which is a pair of IEEE
f744a9
+ * binary64 floats, like on ppc64 systems. This is *not* the hexadecimal
f744a9
+ * IBM-double-double type, which is a pair of IBM hexadecimal64 floats.
f744a9
+ *
f744a9
+ * See also:
f744a9
+ * https://gcc.gnu.org/wiki/Ieee128PowerPCA
f744a9
+ * https://www.ibm.com/support/knowledgecenter/en/ssw_aix_71/com.ibm.aix.genprogc/128bit_long_double_floating-point_datatype.htm
f744a9
+ */
f744a9
+static npy_uint32
f744a9
+Dragon4_PrintFloat_IBM_double_double(
f744a9
+    Dragon4_Scratch *scratch, npy_float128 *value, Dragon4_Options *opt)
f744a9
+{
f744a9
+    char *buffer = scratch->repr;
f744a9
+    npy_uint32 bufferSize = sizeof(scratch->repr);
f744a9
+    BigInt *bigints = scratch->bigints;
f744a9
+
f744a9
+    FloatVal128 val128;
f744a9
+    FloatUnion128 buf128;
f744a9
+
f744a9
+    npy_uint32 floatExponent1, floatExponent2;
f744a9
+    npy_uint64 floatMantissa1, floatMantissa2;
f744a9
+    npy_uint32 floatSign1, floatSign2;
f744a9
+
f744a9
+    npy_uint64 mantissa1, mantissa2;
f744a9
+    npy_int32 exponent1, exponent2;
f744a9
+    int shift;
f744a9
+    npy_uint32 mantissaBit;
f744a9
+    npy_bool hasUnequalMargins;
f744a9
+    char signbit = '\0';
f744a9
+
f744a9
+    if (bufferSize == 0) {
f744a9
+        return 0;
f744a9
+    }
f744a9
+
f744a9
+    if (bufferSize == 1) {
f744a9
+        buffer[0] = '\0';
f744a9
+        return 0;
f744a9
+    }
f744a9
+
f744a9
+    /* The high part always comes before the low part, regardless of the
f744a9
+     * endianness of the system. */
f744a9
+    buf128.floatingPoint = *value;
f744a9
+    val128.hi = buf128.integer.a;
f744a9
+    val128.lo = buf128.integer.b;
f744a9
+
f744a9
+    /* deconstruct the floating point values */
f744a9
+    floatMantissa1 = val128.hi & bitmask_u64(52);
f744a9
+    floatExponent1 = (val128.hi >> 52) & bitmask_u32(11);
f744a9
+    floatSign1 = (val128.hi >> 63) != 0;
f744a9
+
f744a9
+    floatMantissa2 = val128.lo & bitmask_u64(52);
f744a9
+    floatExponent2 = (val128.lo >> 52) & bitmask_u32(11);
f744a9
+    floatSign2 = (val128.lo >> 63) != 0;
f744a9
+
f744a9
+    /* output the sign using 1st float's sign */
f744a9
+    if (floatSign1) {
f744a9
+        signbit = '-';
f744a9
+    }
f744a9
+    else if (opt->sign) {
f744a9
+        signbit = '+';
f744a9
+    }
f744a9
+
f744a9
+    /* we only need to look at the first float for inf/nan */
f744a9
+    if (floatExponent1 == bitmask_u32(11)) {
f744a9
+        return PrintInfNan(buffer, bufferSize, floatMantissa1, 13, signbit);
f744a9
+    }
f744a9
+
f744a9
+    /* else this is a number */
f744a9
+
f744a9
+    /* Factor the 1st value into its parts, see binary64 for comments. */
f744a9
+    if (floatExponent1 == 0) {
f744a9
+        /*
f744a9
+         * If the first number is a subnormal value, the 2nd has to be 0 for
f744a9
+         * the float128 to be normalized, so we can ignore it. In this case
f744a9
+         * the float128 only has the precision of a single binary64 value.
f744a9
+         */
f744a9
+        mantissa1            = floatMantissa1;
f744a9
+        exponent1            = 1 - 1023 - 52;
f744a9
+        mantissaBit          = LogBase2_64(mantissa1);
f744a9
+        hasUnequalMargins    = NPY_FALSE;
f744a9
+
f744a9
+        BigInt_Set_uint64(&bigints[0], mantissa1);
f744a9
+    }
f744a9
+    else {
f744a9
+        mantissa1            = (1ull << 52) | floatMantissa1;
f744a9
+        exponent1            = floatExponent1 - 1023 - 52;
f744a9
+        mantissaBit          = 52 + 53;
f744a9
+
f744a9
+        /*
f744a9
+         * Computing hasUnequalMargins and mantissaBit:
f744a9
+         * This is a little trickier than for IEEE formats.
f744a9
+         *
f744a9
+         * When both doubles are "normal" it is clearer since we can think of
f744a9
+         * it as an IEEE type with a 106 bit mantissa. This value can never
f744a9
+         * have "unequal" margins because of the implied 1 bit in the 2nd
f744a9
+         * value.  (unequal margins only happen when the mantissa has a value
f744a9
+         * like "10000000000...", all zeros except the implied bit at the
f744a9
+         * start, since the next lowest number has a different exponent).
f744a9
+         * mantissaBits will always be 52+53 in this case.
f744a9
+         *
f744a9
+         * If the 1st number is a very small normal, and the 2nd is subnormal
f744a9
+         * and not 2^52 times smaller, the number behaves like a subnormal
f744a9
+         * overall, where the upper number just adds some bits on the left.
f744a9
+         * Like usual subnormals, it has "equal" margins. The slightly tricky
f744a9
+         * thing is that the number of mantissaBits varies. It will be 52
f744a9
+         * (from lower double) plus a variable number depending on the upper
f744a9
+         * number's exponent. We recompute the number of bits in the shift
f744a9
+         * calculation below, because the shift will be equal to the number of
f744a9
+         * lost bits.
f744a9
+         *
f744a9
+         * We can get unequal margins only if the first value has all-0
f744a9
+         * mantissa (except implied bit), and the second value is exactly 0. As
f744a9
+         * a special exception the smallest normal value (smallest exponent, 0
f744a9
+         * mantissa) should have equal margins, since it is "next to" a
f744a9
+         * subnormal value.
f744a9
+         */
f744a9
+
f744a9
+        /* factor the 2nd value into its parts */
f744a9
+        if (floatExponent2 != 0) {
f744a9
+            mantissa2            = (1ull << 52) | floatMantissa2;
f744a9
+            exponent2            = floatExponent2 - 1023 - 52;
f744a9
+            hasUnequalMargins    = NPY_FALSE;
f744a9
+        }
f744a9
+        else {
f744a9
+            /* shift exp by one so that leading mantissa bit is still bit 53 */
f744a9
+            mantissa2            = floatMantissa2 << 1;
f744a9
+            exponent2            = - 1023 - 52;
f744a9
+            hasUnequalMargins  = (floatExponent1 != 1) && (floatMantissa1 == 0)
f744a9
+                                                       && (floatMantissa2 == 0);
f744a9
+        }
f744a9
+
f744a9
+        /*
f744a9
+         * The 2nd val's exponent might not be exactly 52 smaller than the 1st,
f744a9
+         * it can vary a little bit. So do some shifting of the low mantissa,
f744a9
+         * so that the total mantissa is equivalent to bits 53 to 0 of the
f744a9
+         * first double immediately followed by bits 53 to 0 of the second.
f744a9
+         */
f744a9
+        shift = exponent1 - exponent2 - 53;
f744a9
+        if (shift > 0) {
f744a9
+            /* shift more than 64 is undefined behavior */
f744a9
+            mantissa2 = shift < 64 ? mantissa2 >> shift : 0;
f744a9
+        }
f744a9
+        else if (shift < 0) {
f744a9
+            /*
f744a9
+             * This only happens if the 2nd value is subnormal.
f744a9
+             * We expect that shift > -64, but check it anyway
f744a9
+             */
f744a9
+            mantissa2 = -shift < 64 ? mantissa2 << -shift : 0;
f744a9
+        }
f744a9
+
f744a9
+        /*
f744a9
+         * If the low double is a different sign from the high double,
f744a9
+         * rearrange so that the total mantissa is the sum of the two
f744a9
+         * mantissas, instead of a subtraction.
f744a9
+         * hi - lo  ->  (hi-1) + (1-lo),   where lo < 1
f744a9
+         */
f744a9
+        if (floatSign1 != floatSign2 && mantissa2 != 0) {
f744a9
+            mantissa1--;
f744a9
+            mantissa2 = (1ull << 53) - mantissa2;
f744a9
+        }
f744a9
+
f744a9
+        /*
f744a9
+         * Compute the number of bits if we are in the subnormal range.
f744a9
+         * The value "shift" happens to be exactly the number of lost bits.
f744a9
+         * Also, shift the bits so that the least significant bit is at
f744a9
+         * bit position 0, like a typical subnormal. After this exponent1
f744a9
+         * should always be 2^-1022
f744a9
+         */
f744a9
+        if (shift < 0) {
f744a9
+            mantissa2 = (mantissa2 >> -shift) | (mantissa1 << (53 + shift));
f744a9
+            mantissa1 = mantissa1 >> -shift;
f744a9
+            mantissaBit = mantissaBit -(-shift);
f744a9
+            exponent1 -= shift;
f744a9
+            DEBUG_ASSERT(exponent1 == -1022);
f744a9
+        }
f744a9
+
f744a9
+        /*
f744a9
+         * set up the BigInt mantissa, by shifting the parts as needed
f744a9
+         * We can use | instead of + since the mantissas should not overlap
f744a9
+         */
f744a9
+        BigInt_Set_2x_uint64(&bigints[0], mantissa1 >> 11,
f744a9
+                                         (mantissa1 << 53) | (mantissa2));
f744a9
+        exponent1 = exponent1 - 53;
f744a9
     }
f744a9
 
f744a9
-    return PyUString_FromString(repr);
f744a9
+    return Format_floatbits(buffer, bufferSize, bigints, exponent1,
f744a9
+                            signbit, mantissaBit, hasUnequalMargins, opt);
f744a9
 }
f744a9
 
f744a9
+#endif /* HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_LE | HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE */
f744a9
+
f744a9
+#endif /* NPY_FLOAT128 */
f744a9
+
f744a9
+
f744a9
+/*
f744a9
+ * Here we define two Dragon4 entry functions for each type. One of them
f744a9
+ * accepts the args in a Dragon4_Options struct for convenience, the
f744a9
+ * other enumerates only the necessary parameters.
f744a9
+ *
f744a9
+ * Use a very large string buffer in case anyone tries to output a large number.
f744a9
+ * 16384 should be enough to exactly print the integer part of any float128,
f744a9
+ * which goes up to about 10^4932. The Dragon4_scratch struct provides a string
f744a9
+ * buffer of this size.
f744a9
+ */
f744a9
+#define make_dragon4_typefuncs_inner(Type, npy_type, format) \
f744a9
+\
f744a9
+PyObject *\
f744a9
+Dragon4_Positional_##Type##_opt(npy_type *val, Dragon4_Options *opt)\
f744a9
+{\
f744a9
+    PyObject *ret;\
f744a9
+    Dragon4_Scratch *scratch = get_dragon4_bigint_scratch();\
f744a9
+    if (scratch == NULL) {\
f744a9
+        return NULL;\
f744a9
+    }\
f744a9
+    if (Dragon4_PrintFloat_##format(scratch, val, opt) < 0) {\
f744a9
+        free_dragon4_bigint_scratch(scratch);\
f744a9
+        return NULL;\
f744a9
+    }\
f744a9
+    ret = PyUString_FromString(scratch->repr);\
f744a9
+    free_dragon4_bigint_scratch(scratch);\
f744a9
+    return ret;\
f744a9
+}\
f744a9
+\
f744a9
+PyObject *\
f744a9
+Dragon4_Positional_##Type(npy_type *val, DigitMode digit_mode,\
f744a9
+                   CutoffMode cutoff_mode, int precision,\
f744a9
+                   int sign, TrimMode trim, int pad_left, int pad_right)\
f744a9
+{\
f744a9
+    Dragon4_Options opt;\
f744a9
+    \
f744a9
+    opt.scientific = 0;\
f744a9
+    opt.digit_mode = digit_mode;\
f744a9
+    opt.cutoff_mode = cutoff_mode;\
f744a9
+    opt.precision = precision;\
f744a9
+    opt.sign = sign;\
f744a9
+    opt.trim_mode = trim;\
f744a9
+    opt.digits_left = pad_left;\
f744a9
+    opt.digits_right = pad_right;\
f744a9
+    opt.exp_digits = -1;\
f744a9
+\
f744a9
+    return Dragon4_Positional_##Type##_opt(val, &opt;;\
f744a9
+}\
f744a9
+\
f744a9
+PyObject *\
f744a9
+Dragon4_Scientific_##Type##_opt(npy_type *val, Dragon4_Options *opt)\
f744a9
+{\
f744a9
+    PyObject *ret;\
f744a9
+    Dragon4_Scratch *scratch = get_dragon4_bigint_scratch();\
f744a9
+    if (scratch == NULL) {\
f744a9
+        return NULL;\
f744a9
+    }\
f744a9
+    if (Dragon4_PrintFloat_##format(scratch, val, opt) < 0) {\
f744a9
+        free_dragon4_bigint_scratch(scratch);\
f744a9
+        return NULL;\
f744a9
+    }\
f744a9
+    ret = PyUString_FromString(scratch->repr);\
f744a9
+    free_dragon4_bigint_scratch(scratch);\
f744a9
+    return ret;\
f744a9
+}\
f744a9
+PyObject *\
f744a9
+Dragon4_Scientific_##Type(npy_type *val, DigitMode digit_mode, int precision,\
f744a9
+                   int sign, TrimMode trim, int pad_left, int exp_digits)\
f744a9
+{\
f744a9
+    Dragon4_Options opt;\
f744a9
+\
f744a9
+    opt.scientific = 1;\
f744a9
+    opt.digit_mode = digit_mode;\
f744a9
+    opt.cutoff_mode = CutoffMode_TotalLength;\
f744a9
+    opt.precision = precision;\
f744a9
+    opt.sign = sign;\
f744a9
+    opt.trim_mode = trim;\
f744a9
+    opt.digits_left = pad_left;\
f744a9
+    opt.digits_right = -1;\
f744a9
+    opt.exp_digits = exp_digits;\
f744a9
+\
f744a9
+    return Dragon4_Scientific_##Type##_opt(val, &opt;;\
f744a9
+}
f744a9
+
f744a9
+#define make_dragon4_typefuncs(Type, npy_type, format) \
f744a9
+        make_dragon4_typefuncs_inner(Type, npy_type, format)
f744a9
+
f744a9
+make_dragon4_typefuncs(Half, npy_half, NPY_HALF_BINFMT_NAME)
f744a9
+make_dragon4_typefuncs(Float, npy_float, NPY_FLOAT_BINFMT_NAME)
f744a9
+make_dragon4_typefuncs(Double, npy_double, NPY_DOUBLE_BINFMT_NAME)
f744a9
+make_dragon4_typefuncs(LongDouble, npy_longdouble, NPY_LONGDOUBLE_BINFMT_NAME)
f744a9
+
f744a9
+#undef make_dragon4_typefuncs
f744a9
+#undef make_dragon4_typefuncs_inner
f744a9
+
f744a9
 PyObject *
f744a9
 Dragon4_Positional(PyObject *obj, DigitMode digit_mode, CutoffMode cutoff_mode,
f744a9
                    int precision, int sign, TrimMode trim, int pad_left,
f744a9
                    int pad_right)
f744a9
 {
f744a9
-    double val;
f744a9
+    npy_double val;
f744a9
+    Dragon4_Options opt;
f744a9
+
f744a9
+    opt.scientific = 0;
f744a9
+    opt.digit_mode = digit_mode;
f744a9
+    opt.cutoff_mode = cutoff_mode;
f744a9
+    opt.precision = precision;
f744a9
+    opt.sign = sign;
f744a9
+    opt.trim_mode = trim;
f744a9
+    opt.digits_left = pad_left;
f744a9
+    opt.digits_right = pad_right;
f744a9
+    opt.exp_digits = -1;
f744a9
 
f744a9
     if (PyArray_IsScalar(obj, Half)) {
f744a9
         npy_half x = ((PyHalfScalarObject *)obj)->obval;
f744a9
-        return Dragon4_Positional_AnySize(&x, sizeof(npy_half),
f744a9
-                                          digit_mode, cutoff_mode, precision,
f744a9
-                                          sign, trim, pad_left, pad_right);
f744a9
+        return Dragon4_Positional_Half_opt(&x, &opt;;
f744a9
     }
f744a9
     else if (PyArray_IsScalar(obj, Float)) {
f744a9
         npy_float x = ((PyFloatScalarObject *)obj)->obval;
f744a9
-        return Dragon4_Positional_AnySize(&x, sizeof(npy_float),
f744a9
-                                          digit_mode, cutoff_mode, precision,
f744a9
-                                          sign, trim, pad_left, pad_right);
f744a9
+        return Dragon4_Positional_Float_opt(&x, &opt;;
f744a9
     }
f744a9
     else if (PyArray_IsScalar(obj, Double)) {
f744a9
         npy_double x = ((PyDoubleScalarObject *)obj)->obval;
f744a9
-        return Dragon4_Positional_AnySize(&x, sizeof(npy_double),
f744a9
-                                          digit_mode, cutoff_mode, precision,
f744a9
-                                          sign, trim, pad_left, pad_right);
f744a9
+        return Dragon4_Positional_Double_opt(&x, &opt;;
f744a9
     }
f744a9
     else if (PyArray_IsScalar(obj, LongDouble)) {
f744a9
         npy_longdouble x = ((PyLongDoubleScalarObject *)obj)->obval;
f744a9
-        return Dragon4_Positional_AnySize(&x, sizeof(npy_longdouble),
f744a9
-                                          digit_mode, cutoff_mode, precision,
f744a9
-                                          sign, trim, pad_left, pad_right);
f744a9
+        return Dragon4_Positional_LongDouble_opt(&x, &opt;;
f744a9
     }
f744a9
 
f744a9
     val = PyFloat_AsDouble(obj);
f744a9
     if (PyErr_Occurred()) {
f744a9
         return NULL;
f744a9
     }
f744a9
-    return Dragon4_Positional_AnySize(&val, sizeof(double),
f744a9
-                                      digit_mode, cutoff_mode, precision,
f744a9
-                                      sign, trim, pad_left, pad_right);
f744a9
-}
f744a9
-
f744a9
-PyObject *
f744a9
-Dragon4_Scientific_AnySize(void *val, size_t size, DigitMode digit_mode,
f744a9
-                           int precision, int sign, TrimMode trim,
f744a9
-                           int pad_left, int exp_digits)
f744a9
-{
f744a9
-    /* use a very large buffer in case anyone tries to output a large precision */
f744a9
-    static char repr[4096];
f744a9
-    FloatVal128 val128;
f744a9
-#ifdef NPY_FLOAT80
f744a9
-    FloatUnion80 buf80;;
f744a9
-#endif
f744a9
-#ifdef NPY_FLOAT96
f744a9
-    FloatUnion96 buf96;
f744a9
-#endif
f744a9
-#ifdef NPY_FLOAT128
f744a9
-    FloatUnion128 buf128;
f744a9
-#endif
f744a9
-
f744a9
-    /* dummy, is ignored in scientific mode */
f744a9
-    CutoffMode cutoff_mode = CutoffMode_TotalLength;
f744a9
-
f744a9
-    switch (size) {
f744a9
-        case 2:
f744a9
-            Dragon4_PrintFloat16(repr, sizeof(repr), *(npy_float16*)val,
f744a9
-                                 1, digit_mode, cutoff_mode, precision, sign,
f744a9
-                                 trim, pad_left, -1, exp_digits);
f744a9
-            break;
f744a9
-        case 4:
f744a9
-            Dragon4_PrintFloat32(repr, sizeof(repr), *(npy_float32*)val,
f744a9
-                                 1, digit_mode, cutoff_mode, precision, sign,
f744a9
-                                 trim, pad_left, -1, exp_digits);
f744a9
-            break;
f744a9
-        case 8:
f744a9
-            Dragon4_PrintFloat64(repr, sizeof(repr), *(npy_float64*)val,
f744a9
-                                 1, digit_mode, cutoff_mode, precision, sign,
f744a9
-                                 trim, pad_left, -1, exp_digits);
f744a9
-            break;
f744a9
-#ifdef NPY_FLOAT80
f744a9
-        case 10:
f744a9
-            buf80.floatingPoint = *(npy_float80*)val;
f744a9
-            val128.integer[0] = buf80.integer.a;
f744a9
-            val128.integer[1] = buf80.integer.b;
f744a9
-            Dragon4_PrintFloat128(repr, sizeof(repr), val128,
f744a9
-                                  1, digit_mode, cutoff_mode, precision, sign,
f744a9
-                                  trim, pad_left, -1, exp_digits);
f744a9
-            break;
f744a9
-#endif
f744a9
-#ifdef NPY_FLOAT96
f744a9
-        case 12:
f744a9
-            buf96.floatingPoint = *(npy_float96*)val;
f744a9
-            val128.integer[0] = buf96.integer.a;
f744a9
-            val128.integer[1] = buf96.integer.b;
f744a9
-            Dragon4_PrintFloat128(repr, sizeof(repr), val128,
f744a9
-                                  1, digit_mode, cutoff_mode, precision, sign,
f744a9
-                                  trim, pad_left, -1, exp_digits);
f744a9
-            break;
f744a9
-#endif
f744a9
-#ifdef NPY_FLOAT128
f744a9
-        case 16:
f744a9
-            buf128.floatingPoint = *(npy_float128*)val;
f744a9
-            val128.integer[0] = buf128.integer.a;
f744a9
-            val128.integer[1] = buf128.integer.b;
f744a9
-            Dragon4_PrintFloat128(repr, sizeof(repr), val128,
f744a9
-                                  1, digit_mode, cutoff_mode, precision, sign,
f744a9
-                                  trim, pad_left, -1, exp_digits);
f744a9
-            break;
f744a9
-#endif
f744a9
-        default:
f744a9
-            PyErr_Format(PyExc_ValueError, "unexpected itemsize %zu", size);
f744a9
-            return NULL;
f744a9
-    }
f744a9
-
f744a9
-    return PyUString_FromString(repr);
f744a9
+    return Dragon4_Positional_Double_opt(&val, &opt;;
f744a9
 }
f744a9
 
f744a9
 PyObject *
f744a9
 Dragon4_Scientific(PyObject *obj, DigitMode digit_mode, int precision,
f744a9
                    int sign, TrimMode trim, int pad_left, int exp_digits)
f744a9
 {
f744a9
-    double val;
f744a9
+    npy_double val;
f744a9
+    Dragon4_Options opt;
f744a9
+
f744a9
+    opt.scientific = 1;
f744a9
+    opt.digit_mode = digit_mode;
f744a9
+    opt.cutoff_mode = CutoffMode_TotalLength;
f744a9
+    opt.precision = precision;
f744a9
+    opt.sign = sign;
f744a9
+    opt.trim_mode = trim;
f744a9
+    opt.digits_left = pad_left;
f744a9
+    opt.digits_right = -1;
f744a9
+    opt.exp_digits = exp_digits;
f744a9
 
f744a9
     if (PyArray_IsScalar(obj, Half)) {
f744a9
         npy_half x = ((PyHalfScalarObject *)obj)->obval;
f744a9
-        return Dragon4_Scientific_AnySize(&x, sizeof(npy_half),
f744a9
-                                          digit_mode, precision,
f744a9
-                                          sign, trim, pad_left, exp_digits);
f744a9
+        return Dragon4_Scientific_Half_opt(&x, &opt;;
f744a9
     }
f744a9
     else if (PyArray_IsScalar(obj, Float)) {
f744a9
         npy_float x = ((PyFloatScalarObject *)obj)->obval;
f744a9
-        return Dragon4_Scientific_AnySize(&x, sizeof(npy_float),
f744a9
-                                          digit_mode, precision,
f744a9
-                                          sign, trim, pad_left, exp_digits);
f744a9
+        return Dragon4_Scientific_Float_opt(&x, &opt;;
f744a9
     }
f744a9
     else if (PyArray_IsScalar(obj, Double)) {
f744a9
         npy_double x = ((PyDoubleScalarObject *)obj)->obval;
f744a9
-        return Dragon4_Scientific_AnySize(&x, sizeof(npy_double),
f744a9
-                                          digit_mode, precision,
f744a9
-                                          sign, trim, pad_left, exp_digits);
f744a9
+        return Dragon4_Scientific_Double_opt(&x, &opt;;
f744a9
     }
f744a9
     else if (PyArray_IsScalar(obj, LongDouble)) {
f744a9
         npy_longdouble x = ((PyLongDoubleScalarObject *)obj)->obval;
f744a9
-        return Dragon4_Scientific_AnySize(&x, sizeof(npy_longdouble),
f744a9
-                                          digit_mode, precision,
f744a9
-                                          sign, trim, pad_left, exp_digits);
f744a9
+        return Dragon4_Scientific_LongDouble_opt(&x, &opt;;
f744a9
     }
f744a9
 
f744a9
     val = PyFloat_AsDouble(obj);
f744a9
     if (PyErr_Occurred()) {
f744a9
         return NULL;
f744a9
     }
f744a9
-    return Dragon4_Scientific_AnySize(&val, sizeof(double),
f744a9
-                                      digit_mode, precision,
f744a9
-                                      sign, trim, pad_left, exp_digits);
f744a9
+    return Dragon4_Scientific_Double_opt(&val, &opt;;
f744a9
 }
f744a9
+
f744a9
+#undef DEBUG_ASSERT
f744a9
diff --git a/numpy/core/src/multiarray/dragon4.h b/numpy/core/src/multiarray/dragon4.h
f744a9
index 5559c51..2b8b4ce 100644
f744a9
--- a/numpy/core/src/multiarray/dragon4.h
f744a9
+++ b/numpy/core/src/multiarray/dragon4.h
f744a9
@@ -40,6 +40,48 @@
f744a9
 #include "npy_pycompat.h"
f744a9
 #include "numpy/arrayscalars.h"
f744a9
 
f744a9
+/* Half binary format */
f744a9
+#define NPY_HALF_BINFMT_NAME IEEE_binary16
f744a9
+
f744a9
+/* Float binary format */
f744a9
+#if NPY_BITSOF_FLOAT == 32
f744a9
+    #define NPY_FLOAT_BINFMT_NAME IEEE_binary32
f744a9
+#elif NPY_BITSOF_FLOAT == 64
f744a9
+    #define NPY_FLOAT_BINFMT_NAME IEEE_binary64
f744a9
+#else
f744a9
+    #error No float representation defined
f744a9
+#endif
f744a9
+
f744a9
+/* Double binary format */
f744a9
+#if NPY_BITSOF_DOUBLE == 32
f744a9
+    #define NPY_DOUBLE_BINFMT_NAME IEEE_binary32
f744a9
+#elif NPY_BITSOF_DOUBLE == 64
f744a9
+    #define NPY_DOUBLE_BINFMT_NAME IEEE_binary64
f744a9
+#else
f744a9
+    #error No double representation defined
f744a9
+#endif
f744a9
+
f744a9
+/* LongDouble binary format */
f744a9
+#if defined(HAVE_LDOUBLE_IEEE_QUAD_BE)
f744a9
+    #define NPY_LONGDOUBLE_BINFMT_NAME IEEE_binary128_be
f744a9
+#elif defined(HAVE_LDOUBLE_IEEE_QUAD_LE)
f744a9
+    #define NPY_LONGDOUBLE_BINFMT_NAME IEEE_binary128_le
f744a9
+#elif (defined(HAVE_LDOUBLE_IEEE_DOUBLE_LE) || \
f744a9
+       defined(HAVE_LDOUBLE_IEEE_DOUBLE_BE))
f744a9
+    #define NPY_LONGDOUBLE_BINFMT_NAME IEEE_binary64
f744a9
+#elif defined(HAVE_LDOUBLE_INTEL_EXTENDED_12_BYTES_LE)
f744a9
+    #define NPY_LONGDOUBLE_BINFMT_NAME Intel_extended96
f744a9
+#elif defined(HAVE_LDOUBLE_INTEL_EXTENDED_16_BYTES_LE)
f744a9
+    #define NPY_LONGDOUBLE_BINFMT_NAME Intel_extended128
f744a9
+#elif defined(HAVE_LDOUBLE_MOTOROLA_EXTENDED_12_BYTES_BE)
f744a9
+    #define NPY_LONGDOUBLE_BINFMT_NAME Motorola_extended96
f744a9
+#elif (defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_LE) || \
f744a9
+       defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE))
f744a9
+    #define NPY_LONGDOUBLE_BINFMT_NAME IBM_double_double
f744a9
+#else
f744a9
+    #error No long double representation defined
f744a9
+#endif
f744a9
+
f744a9
 typedef enum DigitMode
f744a9
 {
f744a9
     /* Round digits to print shortest uniquely identifiable number. */
f744a9
@@ -64,15 +106,23 @@ typedef enum TrimMode
f744a9
     TrimMode_DptZeros,     /* trim trailing zeros & trailing decimal point */
f744a9
 } TrimMode;
f744a9
 
f744a9
-PyObject *
f744a9
-Dragon4_Positional_AnySize(void *val, size_t size, DigitMode digit_mode,
f744a9
-                           CutoffMode cutoff_mode, int precision, int sign,
f744a9
-                           TrimMode trim, int pad_left, int pad_right);
f744a9
+#define make_dragon4_typedecl(Type, npy_type) \
f744a9
+    PyObject *\
f744a9
+    Dragon4_Positional_##Type(npy_type *val, DigitMode digit_mode,\
f744a9
+                              CutoffMode cutoff_mode, int precision,\
f744a9
+                              int sign, TrimMode trim, int pad_left,\
f744a9
+                              int pad_right);\
f744a9
+    PyObject *\
f744a9
+    Dragon4_Scientific_##Type(npy_type *val, DigitMode digit_mode,\
f744a9
+                              int precision, int sign, TrimMode trim,\
f744a9
+                              int pad_left, int exp_digits);
f744a9
 
f744a9
-PyObject *
f744a9
-Dragon4_Scientific_AnySize(void *val, size_t size, DigitMode digit_mode,
f744a9
-                           int precision, int sign, TrimMode trim,
f744a9
-                           int pad_left, int pad_right);
f744a9
+make_dragon4_typedecl(Half, npy_half)
f744a9
+make_dragon4_typedecl(Float, npy_float)
f744a9
+make_dragon4_typedecl(Double, npy_double)
f744a9
+make_dragon4_typedecl(LongDouble, npy_longdouble)
f744a9
+
f744a9
+#undef make_dragon4_typedecl
f744a9
 
f744a9
 PyObject *
f744a9
 Dragon4_Positional(PyObject *obj, DigitMode digit_mode, CutoffMode cutoff_mode,
f744a9
diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src
f744a9
index 9fc6e17..be2516d 100644
f744a9
--- a/numpy/core/src/multiarray/scalartypes.c.src
f744a9
+++ b/numpy/core/src/multiarray/scalartypes.c.src
f744a9
@@ -432,6 +432,7 @@ gentype_format(PyObject *self, PyObject *args)
f744a9
 
f744a9
 /**begin repeat
f744a9
  * #name = half, float, double, longdouble#
f744a9
+ * #Name = Half, Float, Double, LongDouble#
f744a9
  * #NAME = HALF, FLOAT, DOUBLE, LONGDOUBLE#
f744a9
  * #type = npy_half, npy_float, npy_double, npy_longdouble#
f744a9
  * #suff = h, f, d, l#
f744a9
@@ -443,12 +444,12 @@ format_@name@(@type@ val, npy_bool scientific,
f744a9
               int pad_left, int pad_right, int exp_digits)
f744a9
 {
f744a9
     if (scientific) {
f744a9
-        return Dragon4_Scientific_AnySize(&val, sizeof(@type@),
f744a9
+        return Dragon4_Scientific_@Name@(&val,
f744a9
                         DigitMode_Unique, precision,
f744a9
                         sign, trim, pad_left, exp_digits);
f744a9
     }
f744a9
     else {
f744a9
-        return Dragon4_Positional_AnySize(&val, sizeof(@type@),
f744a9
+        return Dragon4_Positional_@Name@(&val,
f744a9
                         DigitMode_Unique, CutoffMode_TotalLength, precision,
f744a9
                         sign, trim, pad_left, pad_right);
f744a9
     }
f744a9
diff --git a/numpy/core/src/npymath/ieee754.c.src b/numpy/core/src/npymath/ieee754.c.src
f744a9
index 0370ea6..170471f 100644
f744a9
--- a/numpy/core/src/npymath/ieee754.c.src
f744a9
+++ b/numpy/core/src/npymath/ieee754.c.src
f744a9
@@ -183,6 +183,7 @@ static npy_longdouble _nextl(npy_longdouble x, int p)
f744a9
 {
f744a9
     npy_int64 hx,ihx,ilx;
f744a9
     npy_uint64 lx;
f744a9
+    npy_longdouble u;
f744a9
 
f744a9
     GET_LDOUBLE_WORDS64(hx, lx, x);
f744a9
     ihx = hx & 0x7fffffffffffffffLL;      /* |hx| */
f744a9
@@ -193,7 +194,6 @@ static npy_longdouble _nextl(npy_longdouble x, int p)
f744a9
         return x; /* signal the nan */
f744a9
     }
f744a9
     if(ihx == 0 && ilx == 0) {          /* x == 0 */
f744a9
-        npy_longdouble u;
f744a9
         SET_LDOUBLE_WORDS64(x, p, 0ULL);/* return +-minsubnormal */
f744a9
         u = x * x;
f744a9
         if (u == x) {
f744a9
@@ -203,7 +203,6 @@ static npy_longdouble _nextl(npy_longdouble x, int p)
f744a9
         }
f744a9
     }
f744a9
 
f744a9
-    npy_longdouble u;
f744a9
     if(p < 0) { /* p < 0, x -= ulp */
f744a9
         if((hx==0xffefffffffffffffLL)&&(lx==0xfc8ffffffffffffeLL))
f744a9
             return x+x; /* overflow, return -inf */
f744a9
diff --git a/numpy/core/src/npymath/npy_math_private.h b/numpy/core/src/npymath/npy_math_private.h
f744a9
index d75b9e9..e4a919d 100644
f744a9
--- a/numpy/core/src/npymath/npy_math_private.h
f744a9
+++ b/numpy/core/src/npymath/npy_math_private.h
f744a9
@@ -287,8 +287,7 @@ do {                                                            \
f744a9
     typedef npy_uint32 ldouble_man_t;
f744a9
     typedef npy_uint32 ldouble_exp_t;
f744a9
     typedef npy_uint32 ldouble_sign_t;
f744a9
-#elif defined(HAVE_LDOUBLE_IEEE_DOUBLE_16_BYTES_BE) || \
f744a9
-      defined(HAVE_LDOUBLE_IEEE_DOUBLE_BE)
f744a9
+#elif defined(HAVE_LDOUBLE_IEEE_DOUBLE_BE)
f744a9
     /* 64 bits IEEE double precision aligned on 16 bytes: used by ppc arch on
f744a9
      * Mac OS X */
f744a9
 
f744a9
@@ -435,8 +434,8 @@ do {                                                            \
f744a9
     typedef npy_uint32 ldouble_sign_t;
f744a9
 #endif
f744a9
 
f744a9
-#if !defined(HAVE_LDOUBLE_DOUBLE_DOUBLE_BE) && \
f744a9
-    !defined(HAVE_LDOUBLE_DOUBLE_DOUBLE_LE)
f744a9
+#if !defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE) && \
f744a9
+    !defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_LE)
f744a9
 /* Get the sign bit of x. x should be of type IEEEl2bitsrep */
f744a9
 #define GET_LDOUBLE_SIGN(x) \
f744a9
     (((x).a[LDBL_SIGN_INDEX] & LDBL_SIGN_MASK) >> LDBL_SIGN_SHIFT)
f744a9
@@ -477,7 +476,7 @@ do {                                                            \
f744a9
      ((x).a[LDBL_MANH_INDEX] & ~LDBL_MANH_MASK) |                       \
f744a9
      (((IEEEl2bitsrep_part)(v) << LDBL_MANH_SHIFT) & LDBL_MANH_MASK))
f744a9
 
f744a9
-#endif /* #ifndef HAVE_LDOUBLE_DOUBLE_DOUBLE_BE */
f744a9
+#endif /* !HAVE_LDOUBLE_DOUBLE_DOUBLE_* */
f744a9
 
f744a9
 /*
f744a9
  * Those unions are used to convert a pointer of npy_cdouble to native C99
f744a9
diff --git a/numpy/core/src/private/npy_config.h b/numpy/core/src/private/npy_config.h
f744a9
index 107b3cb..8143e77 100644
f744a9
--- a/numpy/core/src/private/npy_config.h
f744a9
+++ b/numpy/core/src/private/npy_config.h
f744a9
@@ -15,7 +15,8 @@
f744a9
  * amd64 is not harmed much by the bloat as the system provides 16 byte
f744a9
  * alignment by default.
f744a9
  */
f744a9
-#if (defined NPY_CPU_X86 || defined _WIN32)
f744a9
+#if (defined NPY_CPU_X86 || defined _WIN32 || defined NPY_CPU_ARMEL_AARCH32 ||\
f744a9
+     defined NPY_CPU_ARMEB_AARCH32)
f744a9
 #define NPY_MAX_COPY_ALIGNMENT 8
f744a9
 #else
f744a9
 #define NPY_MAX_COPY_ALIGNMENT 16
f744a9
diff --git a/numpy/core/src/private/npy_fpmath.h b/numpy/core/src/private/npy_fpmath.h
f744a9
index 86b9cf3..dbb3fb2 100644
f744a9
--- a/numpy/core/src/private/npy_fpmath.h
f744a9
+++ b/numpy/core/src/private/npy_fpmath.h
f744a9
@@ -7,45 +7,24 @@
f744a9
 #include "numpy/npy_cpu.h"
f744a9
 #include "numpy/npy_common.h"
f744a9
 
f744a9
-#ifdef NPY_OS_DARWIN
f744a9
-    /* This hardcoded logic is fragile, but universal builds makes it
f744a9
-     * difficult to detect arch-specific features */
f744a9
-
f744a9
-    /* MAC OS X < 10.4 and gcc < 4 does not support proper long double, and
f744a9
-     * is the same as double on those platforms */
f744a9
-    #if NPY_BITSOF_LONGDOUBLE == NPY_BITSOF_DOUBLE
f744a9
-        /* This assumes that FPU and ALU have the same endianness */
f744a9
-        #if NPY_BYTE_ORDER == NPY_LITTLE_ENDIAN
f744a9
-            #define HAVE_LDOUBLE_IEEE_DOUBLE_LE
f744a9
-        #elif NPY_BYTE_ORDER == NPY_BIG_ENDIAN
f744a9
-            #define HAVE_LDOUBLE_IEEE_DOUBLE_BE
f744a9
-        #else
f744a9
-            #error Endianness undefined ?
f744a9
-        #endif
f744a9
-    #else
f744a9
-        #if defined(NPY_CPU_X86)
f744a9
-            #define HAVE_LDOUBLE_INTEL_EXTENDED_12_BYTES_LE
f744a9
-        #elif defined(NPY_CPU_AMD64)
f744a9
-            #define HAVE_LDOUBLE_INTEL_EXTENDED_16_BYTES_LE
f744a9
-        #elif defined(NPY_CPU_PPC) || defined(NPY_CPU_PPC64)
f744a9
-            #define HAVE_LDOUBLE_IEEE_DOUBLE_16_BYTES_BE
f744a9
-        #elif defined(NPY_CPU_PPC64LE)
f744a9
-            #define HAVE_LDOUBLE_IEEE_DOUBLE_16_BYTES_LE
f744a9
-        #endif
f744a9
-    #endif
f744a9
-#endif
f744a9
-
f744a9
 #if !(defined(HAVE_LDOUBLE_IEEE_QUAD_BE) || \
f744a9
       defined(HAVE_LDOUBLE_IEEE_QUAD_LE) || \
f744a9
       defined(HAVE_LDOUBLE_IEEE_DOUBLE_LE) || \
f744a9
       defined(HAVE_LDOUBLE_IEEE_DOUBLE_BE) || \
f744a9
-      defined(HAVE_LDOUBLE_IEEE_DOUBLE_16_BYTES_BE) || \
f744a9
       defined(HAVE_LDOUBLE_INTEL_EXTENDED_16_BYTES_LE) || \
f744a9
       defined(HAVE_LDOUBLE_INTEL_EXTENDED_12_BYTES_LE) || \
f744a9
       defined(HAVE_LDOUBLE_MOTOROLA_EXTENDED_12_BYTES_BE) || \
f744a9
-      defined(HAVE_LDOUBLE_DOUBLE_DOUBLE_BE) || \
f744a9
-      defined(HAVE_LDOUBLE_DOUBLE_DOUBLE_LE))
f744a9
+      defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE) || \
f744a9
+      defined(HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_LE))
f744a9
     #error No long double representation defined
f744a9
 #endif
f744a9
 
f744a9
+/* for back-compat, also keep old name for double-double */
f744a9
+#ifdef HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_LE
f744a9
+    #define HAVE_LDOUBLE_DOUBLE_DOUBLE_LE
f744a9
+#endif
f744a9
+#ifdef HAVE_LDOUBLE_IBM_DOUBLE_DOUBLE_BE
f744a9
+    #define HAVE_LDOUBLE_DOUBLE_DOUBLE_BE
f744a9
+#endif
f744a9
+
f744a9
 #endif
f744a9
diff --git a/numpy/core/tests/test_scalarprint.py b/numpy/core/tests/test_scalarprint.py
f744a9
index d57f1a8..e04de61 100644
f744a9
--- a/numpy/core/tests/test_scalarprint.py
f744a9
+++ b/numpy/core/tests/test_scalarprint.py
f744a9
@@ -5,7 +5,8 @@
f744a9
 from __future__ import division, absolute_import, print_function
f744a9
 
f744a9
 import numpy as np
f744a9
-from numpy.testing import assert_, assert_equal, run_module_suite
f744a9
+from numpy.testing import assert_, assert_equal, run_module_suite, dec
f744a9
+import platform
f744a9
 
f744a9
 
f744a9
 class TestRealScalars(object):
f744a9
@@ -201,6 +202,66 @@ class TestRealScalars(object):
f744a9
                          "1.2" if tp != np.float16 else "1.2002")
f744a9
             assert_equal(fpos(tp('1.'), trim='-'), "1")
f744a9
 
f744a9
+    @dec.skipif(not platform.machine().startswith("ppc64"),
f744a9
+                "only applies to ppc float128 values")
f744a9
+    def test_ppc64_ibm_double_double128(self):
f744a9
+        # check that the precision decreases once we get into the subnormal
f744a9
+        # range. Unlike float64, this starts around 1e-292 instead of 1e-308,
f744a9
+        # which happens when the first double is normal and the second is
f744a9
+        # subnormal.
f744a9
+        x = np.float128('2.123123123123123123123123123123123e-286')
f744a9
+        got = [str(x/np.float128('2e' + str(i))) for i in range(0,40)]
f744a9
+        expected = [
f744a9
+            "1.06156156156156156156156156156157e-286",
f744a9
+            "1.06156156156156156156156156156158e-287",
f744a9
+            "1.06156156156156156156156156156159e-288",
f744a9
+            "1.0615615615615615615615615615616e-289",
f744a9
+            "1.06156156156156156156156156156157e-290",
f744a9
+            "1.06156156156156156156156156156156e-291",
f744a9
+            "1.0615615615615615615615615615616e-292",
f744a9
+            "1.0615615615615615615615615615615e-293",
f744a9
+            "1.061561561561561561561561561562e-294",
f744a9
+            "1.06156156156156156156156156155e-295",
f744a9
+            "1.0615615615615615615615615616e-296",
f744a9
+            "1.06156156156156156156156156e-297",
f744a9
+            "1.06156156156156156156156157e-298",
f744a9
+            "1.0615615615615615615615616e-299",
f744a9
+            "1.06156156156156156156156e-300",
f744a9
+            "1.06156156156156156156155e-301",
f744a9
+            "1.0615615615615615615616e-302",
f744a9
+            "1.061561561561561561562e-303",
f744a9
+            "1.06156156156156156156e-304",
f744a9
+            "1.0615615615615615618e-305",
f744a9
+            "1.06156156156156156e-306",
f744a9
+            "1.06156156156156157e-307",
f744a9
+            "1.0615615615615616e-308",
f744a9
+            "1.06156156156156e-309",
f744a9
+            "1.06156156156157e-310",
f744a9
+            "1.0615615615616e-311",
f744a9
+            "1.06156156156e-312",
f744a9
+            "1.06156156154e-313",
f744a9
+            "1.0615615616e-314",
f744a9
+            "1.06156156e-315",
f744a9
+            "1.06156155e-316",
f744a9
+            "1.061562e-317",
f744a9
+            "1.06156e-318",
f744a9
+            "1.06155e-319",
f744a9
+            "1.0617e-320",
f744a9
+            "1.06e-321",
f744a9
+            "1.04e-322",
f744a9
+            "1e-323",
f744a9
+            "0.0",
f744a9
+            "0.0"]
f744a9
+        assert_equal(got, expected)
f744a9
+
f744a9
+        # Note: we follow glibc behavior, but it (or gcc) might not be right.
f744a9
+        # In particular we can get two values that print the same but are not
f744a9
+        # equal:
f744a9
+        a = np.float128('2')/np.float128('3')
f744a9
+        b = np.float128(str(a))
f744a9
+        assert_equal(str(a), str(b))
f744a9
+        assert_(a != b)
f744a9
+
f744a9
     def float32_roundtrip(self):
f744a9
         # gh-9360
f744a9
         x = np.float32(1024 - 2**-14)
f744a9
diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py
f744a9
index bebeddc..09d1bbe 100644
f744a9
--- a/numpy/core/tests/test_umath.py
f744a9
+++ b/numpy/core/tests/test_umath.py
f744a9
@@ -2515,8 +2515,10 @@ def test_nextafter():
f744a9
 def test_nextafterf():
f744a9
     return _test_nextafter(np.float32)
f744a9
 
f744a9
-@dec.knownfailureif(sys.platform == 'win32',
f744a9
-            "Long double support buggy on win32, ticket 1664.")
f744a9
+@dec.skipif(np.finfo(np.double) == np.finfo(np.longdouble),
f744a9
+            "long double is same as double")
f744a9
+@dec.knownfailureif(platform.machine().startswith("ppc64"),
f744a9
+                    "IBM double double")
f744a9
 def test_nextafterl():
f744a9
     return _test_nextafter(np.longdouble)
f744a9
 
f744a9
@@ -2544,8 +2546,10 @@ def test_spacing():
f744a9
 def test_spacingf():
f744a9
     return _test_spacing(np.float32)
f744a9
 
f744a9
-@dec.knownfailureif(sys.platform == 'win32',
f744a9
-            "Long double support buggy on win32, ticket 1664.")
f744a9
+@dec.skipif(np.finfo(np.double) == np.finfo(np.longdouble),
f744a9
+            "long double is same as double")
f744a9
+@dec.knownfailureif(platform.machine().startswith("ppc64"),
f744a9
+                    "IBM double double")
f744a9
 def test_spacingl():
f744a9
     return _test_spacing(np.longdouble)
f744a9
 
f744a9
diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py
f744a9
index 708c12e..1b47021 100644
f744a9
--- a/numpy/ma/tests/test_core.py
f744a9
+++ b/numpy/ma/tests/test_core.py
f744a9
@@ -3278,18 +3278,13 @@ class TestMaskedArrayMethods(object):
f744a9
         assert_equal(test.mask, mask_first.mask)
f744a9
 
f744a9
         # Test sort on dtype with subarray (gh-8069)
f744a9
+        # Just check that the sort does not error, structured array subarrays
f744a9
+        # are treated as byte strings and that leads to differing behavior
f744a9
+        # depending on endianess and `endwith`.
f744a9
         dt = np.dtype([('v', int, 2)])
f744a9
         a = a.view(dt)
f744a9
-        mask_last = mask_last.view(dt)
f744a9
-        mask_first = mask_first.view(dt)
f744a9
-
f744a9
         test = sort(a)
f744a9
-        assert_equal(test, mask_last)
f744a9
-        assert_equal(test.mask, mask_last.mask)
f744a9
-
f744a9
         test = sort(a, endwith=False)
f744a9
-        assert_equal(test, mask_first)
f744a9
-        assert_equal(test.mask, mask_first.mask)
f744a9
 
f744a9
     def test_argsort(self):
f744a9
         # Test argsort
f744a9
-- 
f744a9
2.21.0
f744a9