chiark / gitweb /
@@@ tty wip
[mLib] / utils / fltfmt.c
index deaf553667bbaee00acd68673766ed64852640a2..177004e89c1532f6396f9010c48d2ac48a065833 100644 (file)
 #include "bits.h"
 #include "fltfmt.h"
 #include "growbuf.h"
 #include "bits.h"
 #include "fltfmt.h"
 #include "growbuf.h"
-#include "macros.h"
 #include "maths.h"
 
 #include "maths.h"
 
+/*----- Preliminary hacking -----------------------------------------------*/
+
+/* The native-format conversions are -- at least if the format is
+ * unrecognized -- dependent on the implementation's rounding.  Our own
+ * rounding mode specifications don't fit into the framework very well, but I
+ * still want to respect the prevailing rounding mode.
+ *
+ * The `proper' way to do this is with %|#pragma STDC FENV_ACCESS|%.  But
+ * that doesn't actually work on GCC, or on Clang from not too long ago.  So
+ * use compiler-specific hacking to support this.
+ */
 #if GCC_VERSION_P(4, 4)
 #  pragma GCC optimize "-frounding-math"
 #elif CLANG_VERSION_P(11, 0) && !CLANG_VERSION_P(12, 0)
 #if GCC_VERSION_P(4, 4)
 #  pragma GCC optimize "-frounding-math"
 #elif CLANG_VERSION_P(11, 0) && !CLANG_VERSION_P(12, 0)
@@ -291,7 +301,7 @@ static unsigned ms_set_bit(const uint32 *x, unsigned from, unsigned to)
    */
 
   /* If the region is empty then it's technically true that all of the bits
    */
 
   /* If the region is empty then it's technically true that all of the bits
-   * are zero.  It's important to be able to do answer the case where
+   * are zero.  It's important to be able to answer the case where
    * %$\id{from} = \id{to} = 0$% without accessing memory.
    */
   assert(to >= from); if (to == from) return (ALLCLEAR);
    * %$\id{from} = \id{to} = 0$% without accessing memory.
    */
   assert(to >= from); if (to == from) return (ALLCLEAR);
@@ -531,7 +541,7 @@ unsigned fltfmt_round(struct floatbits *z_out, const struct floatbits *x,
   return (rc);
 }
 
   return (rc);
 }
 
-/*----- IEEE formats ------------------------------------------------------*/
+/*----- IEEE and related formats ------------------------------------------*/
 
 /* IEEE (and related) format descriptions. */
 const struct fltfmt_ieeefmt
 
 /* IEEE (and related) format descriptions. */
 const struct fltfmt_ieeefmt
@@ -622,16 +632,24 @@ unsigned fltfmt_encieee(const struct fltfmt_ieeefmt *fmt,
     /* Copy the payload.
      *
      * If the payload is all-zero and we're meant to set a signalling NaN
     /* Copy the payload.
      *
      * If the payload is all-zero and we're meant to set a signalling NaN
-     * then report an exactness failure and set the low bit.
+     * then report an exactness failure and set the least-significant bit.
      */
     mb = fmt->prec - 2; mw = (mb + 31)/32; sh = -mb%32;
      */
     mb = fmt->prec - 2; mw = (mb + 31)/32; sh = -mb%32;
-    for (i = 0; i < nw - mw; i++) z[i] = 0;
-    n = x->n; if (n > mw) n = nw;
-    t = shr(z + i, x->frac, n, sh); i += n;
-    if (i < nw) z[i++] = t;
-    sh = esh - 2; if (fmt->f&FLTIF_HIDDEN) sh++;
-    if (f&FLTF_QNAN) z0 |= B32(sh);
-    else if (!fracwd) { ERR(FLTERR_INEXACT); z[nw - 1] |= 1; }
+    n = x->n;
+      if (n < mw) j = 0;
+      else { n = mw; j = sh; }
+    if ((f&FLTF_SNAN) && ms_set_bit(x->frac + n, j, 32*n) == ALLCLEAR) {
+      ERR(FLTERR_INEXACT);
+      n = nw - 1; for (i = 0; i < n; i++) z[i] = 0;
+      z[i++] = 1;
+    } else {
+      for (i = 0; i < nw - mw; i++) z[i] = 0;
+      n = x->n; if (n > mw) n = mw;
+      t = shr(z + i, x->frac, n, sh); i += n;
+      if (i < nw) z[i++] = t;
+      sh = esh - 2; if (fmt->f&FLTIF_HIDDEN) sh++;
+      if (f&FLTF_QNAN) z0 |= B32(sh);
+    }
 
     /* Set the exponent and, for non-hidden-bit formats, the unit bit. */
     z0 |= M32(fmt->expwd) << esh;
 
     /* Set the exponent and, for non-hidden-bit formats, the unit bit. */
     z0 |= M32(fmt->expwd) << esh;
@@ -739,7 +757,9 @@ unsigned fltfmt_encieee(const struct fltfmt_ieeefmt *fmt,
        /* Set the biased exponent. */
        z0 |= (exp + maxexp) << esh;
 
        /* Set the biased exponent. */
        z0 |= (exp + maxexp) << esh;
 
-       /* Clear the unit bit if we're suppose to use a hidden-bit convention. */
+       /* Clear the unit bit if we're suppose to use a hidden-bit
+        * convention.
+        */
        if (fmt->f&FLTIF_HIDDEN) {
          mb = fmt->prec - 1; mw = (mb + 31)/32; mb = mb%32;
          z[nw - mw] &= ~B32(mb);
        if (fmt->f&FLTIF_HIDDEN) {
          mb = fmt->prec - 1; mw = (mb + 31)/32; mb = mb%32;
          z[nw - mw] &= ~B32(mb);
@@ -1123,6 +1143,13 @@ unsigned fltfmt_decidblext80(struct floatbits *z_out, uint16 se, kludge64 m)
 #  define DIGIT_BITS 4
 #endif
 
 #  define DIGIT_BITS 4
 #endif
 
+/* Take note if we need to cope with the revered quiet/signalling convention
+ * used by HP-PA and older MIPS processors.
+ */
+#if defined(__hppa__) || (defined(__mips__) && !defined(__mips_nan2008))
+#  define FROB_NANS
+#endif
+
 /* --- @ENCFLT@ --- *
  *
  * Arguments:  @ty@ = the C type to encode
 /* --- @ENCFLT@ --- *
  *
  * Arguments:  @ty@ = the C type to encode
@@ -1178,8 +1205,10 @@ unsigned fltfmt_decidblext80(struct floatbits *z_out, uint16 se, kludge64 m)
     * requisite compile-time configuration machinery is worth the effort.)
     */
 
     * requisite compile-time configuration machinery is worth the effort.)
     */
 
-#  define SETINF(TY, rc, z)                                            \
-       do { (z) = TY##_MAX; (rc) = FLTERR_OFLOW | FLTERR_INEXACT; } while (0)
+#  define SETINF(TY, rc, z) do {                                       \
+       (z) = TY##_MAX;                                                 \
+       (rc) = FLTERR_OFLOW | FLTERR_INEXACT;                           \
+   } while (0)
 #endif
 
 #ifdef DIGIT_BITS
 #endif
 
 #ifdef DIGIT_BITS
@@ -1205,8 +1234,33 @@ unsigned fltfmt_decidblext80(struct floatbits *z_out, uint16 se, kludge64 m)
 
 #endif
 
 
 #endif
 
+#ifdef FROB_NANS
+   /* The native floating point format uses the opposite quiet-vs-signalling
+    * NaN convention from the recommended `quiet bit' convention, so the bit
+    * needs hacking on input.
+    */
+
+#  define FROBNAN_ENCDECLS     struct floatbits _y
+#  define FROBNAN_ENC do {                                             \
+     if (_x->f&FLTF_NANMASK) {                                         \
+       _y.f = _x->f ^ FLTF_NANMASK; _y.frac = _x->frac; _y.n = _x->n;  \
+       _x = &_y;                                                       \
+     }                                                                 \
+   } while (0)
+#else
+   /* The native floating point format either uses the conventional
+    * `quiet-bit' convention, or isn't IEEE at all.  Either way, there's
+    * nothing to do here.
+    */
+
+#  define FROBNAN_ENCDECLS
+#  define FROBNAN_ENC do ; while (0)
+#endif
+
 #define ENCFLT(ty, TY, ldexp, rc, z_out, x, r) do {                    \
 #define ENCFLT(ty, TY, ldexp, rc, z_out, x, r) do {                    \
+  const struct floatbits *_x = (x);                                    \
   unsigned _rc = 0;                                                    \
   unsigned _rc = 0;                                                    \
+  FROBNAN_ENCDECLS;                                                    \
                                                                        \
   /* See if the native format is one that we recognize. */             \
   switch (TY##_FORMAT&(FLTFMT_ORGMASK | FLTFMT_TYPEMASK)) {            \
                                                                        \
   /* See if the native format is one that we recognize. */             \
   switch (TY##_FORMAT&(FLTFMT_ORGMASK | FLTFMT_TYPEMASK)) {            \
@@ -1215,8 +1269,8 @@ unsigned fltfmt_decidblext80(struct floatbits *z_out, uint16 se, kludge64 m)
       uint32 _t[1];                                                    \
       unsigned char *_z = (unsigned char *)(z_out);                    \
                                                                        \
       uint32 _t[1];                                                    \
       unsigned char *_z = (unsigned char *)(z_out);                    \
                                                                        \
-      (rc) = fltfmt_encieee(&fltfmt_f32, _t, (x), (r), FLTERR_ALLERRS);        \
-      FLTFMT__FROB_NAN_F32(_t, _rc);                                   \
+      FROBNAN_ENC;                                                     \
+      (rc) = fltfmt_encieee(&fltfmt_f32, _t, _x, (r), FLTERR_ALLERRS); \
       switch (TY##_FORMAT&FLTFMT_ENDMASK) {                            \
        case FLTFMT_BE: STORE32_B(_z, _t[0]); break;                    \
        case FLTFMT_LE: STORE32_L(_z, _t[0]); break;                    \
       switch (TY##_FORMAT&FLTFMT_ENDMASK) {                            \
        case FLTFMT_BE: STORE32_B(_z, _t[0]); break;                    \
        case FLTFMT_LE: STORE32_L(_z, _t[0]); break;                    \
@@ -1227,8 +1281,9 @@ unsigned fltfmt_decidblext80(struct floatbits *z_out, uint16 se, kludge64 m)
     case FLTFMT_IEEE_F64: {                                            \
       uint32 _t[2];                                                    \
       unsigned char *_z = (unsigned char *)(z_out);                    \
     case FLTFMT_IEEE_F64: {                                            \
       uint32 _t[2];                                                    \
       unsigned char *_z = (unsigned char *)(z_out);                    \
-      (rc) = fltfmt_encieee(&fltfmt_f64, _t, (x), (r), FLTERR_ALLERRS);        \
-      FLTFMT__FROB_NAN_F64(_t, _rc);                                   \
+                                                                       \
+      FROBNAN_ENC;                                                     \
+      (rc) = fltfmt_encieee(&fltfmt_f64, _t, _x, (r), FLTERR_ALLERRS); \
       switch (TY##_FORMAT&FLTFMT_ENDMASK) {                            \
        case FLTFMT_BE:                                                 \
          STORE32_B(_z + 0, _t[0]); STORE32_B(_z + 4, _t[1]);           \
       switch (TY##_FORMAT&FLTFMT_ENDMASK) {                            \
        case FLTFMT_BE:                                                 \
          STORE32_B(_z + 0, _t[0]); STORE32_B(_z + 4, _t[1]);           \
@@ -1247,8 +1302,8 @@ unsigned fltfmt_decidblext80(struct floatbits *z_out, uint16 se, kludge64 m)
       uint32 _t[4];                                                    \
       unsigned char *_z = (unsigned char *)(z_out);                    \
                                                                        \
       uint32 _t[4];                                                    \
       unsigned char *_z = (unsigned char *)(z_out);                    \
                                                                        \
-      FLTFMT__FROB_NAN_F128(_t, _rc);                                  \
-      (rc) = fltfmt_encieee(&fltfmt_f128, _t, (x), (r), FLTERR_ALLERRS); \
+      FROBNAN_ENC;                                                     \
+      (rc) = fltfmt_encieee(&fltfmt_f128, _t, _x, (r), FLTERR_ALLERRS);        \
       switch (TY##_FORMAT&FLTFMT_ENDMASK) {                            \
        case FLTFMT_BE:                                                 \
          STORE32_B(_z +  0, _t[0]); STORE32_B(_z +  4, _t[1]);         \
       switch (TY##_FORMAT&FLTFMT_ENDMASK) {                            \
        case FLTFMT_BE:                                                 \
          STORE32_B(_z +  0, _t[0]); STORE32_B(_z +  4, _t[1]);         \
@@ -1266,8 +1321,9 @@ unsigned fltfmt_decidblext80(struct floatbits *z_out, uint16 se, kludge64 m)
       uint32 _t[3];                                                    \
       unsigned char *_z = (unsigned char *)(z_out);                    \
                                                                        \
       uint32 _t[3];                                                    \
       unsigned char *_z = (unsigned char *)(z_out);                    \
                                                                        \
-      (rc) = fltfmt_encieee(&fltfmt_idblext80, _t, (x), (r), FLTERR_ALLERRS); \
-      FLTFMT__FROB_NAN_IDBLEXT80(_t, _rc);                             \
+      FROBNAN_ENC;                                                     \
+      (rc) = fltfmt_encieee(&fltfmt_idblext80,                         \
+                           _t, _x, (r), FLTERR_ALLERRS);               \
       switch (TY##_FORMAT&FLTFMT_ENDMASK) {                            \
        case FLTFMT_BE:                                                 \
          STORE16_B(_z + 0, _t[0]);                                     \
       switch (TY##_FORMAT&FLTFMT_ENDMASK) {                            \
        case FLTFMT_BE:                                                 \
          STORE16_B(_z + 0, _t[0]);                                     \
@@ -1284,7 +1340,6 @@ unsigned fltfmt_decidblext80(struct floatbits *z_out, uint16 se, kludge64 m)
     default: {                                                         \
       /* We must do this the hard way. */                              \
                                                                        \
     default: {                                                         \
       /* We must do this the hard way. */                              \
                                                                        \
-      const struct floatbits *_x = (x);                                        \
       ty _z;                                                           \
       unsigned _i;                                                     \
       ENC_ROUND_DECLS;                                                 \
       ty _z;                                                           \
       unsigned _i;                                                     \
       ENC_ROUND_DECLS;                                                 \
@@ -1371,7 +1426,7 @@ unsigned fltfmt_decidblext80(struct floatbits *z_out, uint16 se, kludge64 m)
  *                 zero.
  *
  *               * If the implementation's floating-point radix is not a
  *                 zero.
  *
  *               * If the implementation's floating-point radix is not a
- *                 power of two, and @x@ is a nonzero finite value, then
+ *                 power of two, and @x@ is a nonzero finite value, then the
  *                 @FLTERR_INEXACT@ error bit is set (unconditionally), and
  *                 the value is rounded by the implementation using its
  *                 prevailing rounding policy.  If the radix is a power of
  *                 @FLTERR_INEXACT@ error bit is set (unconditionally), and
  *                 the value is rounded by the implementation using its
  *                 prevailing rounding policy.  If the radix is a power of
@@ -1466,7 +1521,26 @@ unsigned fltfmt_encldbl(long double *z_out,
    } while (0)
 #endif
 
    } while (0)
 #endif
 
+#ifdef FROB_NANS
+   /* The native floating point format uses the opposite quiet-vs-signalling
+    * NaN convention from the recommended `quiet bit' convention, so the bit
+    * needs hacking on output.
+    */
+
+#  define FROBNAN_DEC do {                                             \
+     if (_z->f&FLTF_NANMASK) _z->f ^= FLTF_NANMASK;                    \
+   } while (0)
+#else
+   /* The native floating point format either uses the conventional
+    * `quiet-bit' convention, or isn't IEEE at all.  Either way, there's
+    * nothing to do here.
+    */
+
+#  define FROBNAN_DEC do ; while (0)
+#endif
+
 #define DECFLT(ty, TY, frexp, rc, z_out, x, r) do {                    \
 #define DECFLT(ty, TY, frexp, rc, z_out, x, r) do {                    \
+  struct floatbits *_z = (z_out);                                      \
   unsigned _rc = 0;                                                    \
                                                                        \
   switch (TY##_FORMAT&(FLTFMT_ORGMASK | FLTFMT_TYPEMASK)) {            \
   unsigned _rc = 0;                                                    \
                                                                        \
   switch (TY##_FORMAT&(FLTFMT_ORGMASK | FLTFMT_TYPEMASK)) {            \
@@ -1480,8 +1554,7 @@ unsigned fltfmt_encldbl(long double *z_out,
        case FLTFMT_LE: _t[0] = LOAD32_L(_x); break;                    \
        default: assert(!"unimplemented byte order"); break;            \
       }                                                                        \
        case FLTFMT_LE: _t[0] = LOAD32_L(_x); break;                    \
        default: assert(!"unimplemented byte order"); break;            \
       }                                                                        \
-      FLTFMT__FROB_NAN_F32(_t, _rc);                                   \
-      _rc |= fltfmt_decieee(&fltfmt_f32, (z_out), _t);                 \
+      _rc |= fltfmt_decieee(&fltfmt_f32, _z, _t); FROBNAN_DEC;         \
     } break;                                                           \
                                                                        \
     case FLTFMT_IEEE_F64: {                                            \
     } break;                                                           \
                                                                        \
     case FLTFMT_IEEE_F64: {                                            \
@@ -1500,8 +1573,7 @@ unsigned fltfmt_encldbl(long double *z_out,
          break;                                                        \
        default: assert(!"unimplemented byte order"); break;            \
       }                                                                        \
          break;                                                        \
        default: assert(!"unimplemented byte order"); break;            \
       }                                                                        \
-      FLTFMT__FROB_NAN_F64(_t, _rc);                                   \
-      _rc |= fltfmt_decieee(&fltfmt_f64, (z_out), _t);                 \
+      _rc |= fltfmt_decieee(&fltfmt_f64, _z, _t); FROBNAN_DEC;         \
     } break;                                                           \
                                                                        \
     case FLTFMT_IEEE_F128: {                                           \
     } break;                                                           \
                                                                        \
     case FLTFMT_IEEE_F128: {                                           \
@@ -1519,8 +1591,7 @@ unsigned fltfmt_encldbl(long double *z_out,
          break;                                                        \
        default: assert(!"unimplemented byte order"); break;            \
       }                                                                        \
          break;                                                        \
        default: assert(!"unimplemented byte order"); break;            \
       }                                                                        \
-      FLTFMT__FROB_NAN_F128(_t, _rc);                                  \
-      _rc |= fltfmt_decieee(&fltfmt_f128, (z_out), _t);                        \
+      _rc |= fltfmt_decieee(&fltfmt_f128, _z, _t); FROBNAN_DEC;                \
     } break;                                                           \
                                                                        \
     case FLTFMT_INTEL_F80: {                                           \
     } break;                                                           \
                                                                        \
     case FLTFMT_INTEL_F80: {                                           \
@@ -1538,12 +1609,10 @@ unsigned fltfmt_encldbl(long double *z_out,
          break;                                                        \
        default: assert(!"unimplemented byte order"); break;            \
       }                                                                        \
          break;                                                        \
        default: assert(!"unimplemented byte order"); break;            \
       }                                                                        \
-      FLTFMT__FROB_NAN_IDBLEXT80(_t, _rc);                             \
-      _rc |= fltfmt_decieee(&fltfmt_idblext80, (z_out), _t);           \
+      _rc |= fltfmt_decieee(&fltfmt_idblext80, _z, _t); FROBNAN_DEC;   \
     } break;                                                           \
                                                                        \
     default: {                                                         \
     } break;                                                           \
                                                                        \
     default: {                                                         \
-      struct floatbits *_z = (z_out);                                  \
       ty _x = (x), _y;                                                 \
       unsigned _i, _n, _f = 0;                                         \
       uint32 _t;                                                       \
       ty _x = (x), _y;                                                 \
       unsigned _i, _n, _f = 0;                                         \
       uint32 _t;                                                       \
@@ -1593,7 +1662,7 @@ unsigned fltfmt_encldbl(long double *z_out,
  *
  * Returns:    Error flags (@FLTERR_...@).
  *
  *
  * Returns:    Error flags (@FLTERR_...@).
  *
- * Use:                Decode the native C floatingpoint value @x@ and store the
+ * Use:                Decode the native C floating-point value @x@ and store the
  *             result in @z_out@.
  *
  *             The @TY@ may be @flt@ to encode a @float@, @dbl@ to encode a
  *             result in @z_out@.
  *
  *             The @TY@ may be @flt@ to encode a @float@, @dbl@ to encode a
@@ -1610,7 +1679,7 @@ unsigned fltfmt_encldbl(long double *z_out,
  *                 error bit is set and the decoded payload is left empty.
  *
  *               * If the implementation's floating-point radix is not a
  *                 error bit is set and the decoded payload is left empty.
  *
  *               * If the implementation's floating-point radix is not a
- *                 power of two, and @x@ is a nonzero finite value, then
+ *                 power of two, and @x@ is a nonzero finite value, then the
  *                 @FLTERR_INEXACT@ error bit is set (unconditionally), and
  *                 the rounded value (according to the rounding mode @r@) is
  *                 stored in as many fraction words as necessary to identify
  *                 @FLTERR_INEXACT@ error bit is set (unconditionally), and
  *                 the rounded value (according to the rounding mode @r@) is
  *                 stored in as many fraction words as necessary to identify