chiark / gitweb /
@@@ fltfmt wip
[mLib] / utils / fltfmt.c
index deaf553667bbaee00acd68673766ed64852640a2..cb39eca4cbaa145f87ecc360fd7b3eb735f763b1 100644 (file)
 #include "bits.h"
 #include "fltfmt.h"
 #include "growbuf.h"
-#include "macros.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)
@@ -531,7 +541,7 @@ unsigned fltfmt_round(struct floatbits *z_out, const struct floatbits *x,
   return (rc);
 }
 
-/*----- IEEE formats ------------------------------------------------------*/
+/*----- IEEE and related formats ------------------------------------------*/
 
 /* 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
-     * 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;
-    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;
@@ -1123,6 +1141,13 @@ unsigned fltfmt_decidblext80(struct floatbits *z_out, uint16 se, kludge64 m)
 #  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
@@ -1205,8 +1230,23 @@ unsigned fltfmt_decidblext80(struct floatbits *z_out, uint16 se, kludge64 m)
 
 #endif
 
+#ifdef FROB_NANS
+#  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
+#  define FROBNAN_ENCDECLS
+#  define FROBNAN_ENC do ; while (0)
+#endif
+
 #define ENCFLT(ty, TY, ldexp, rc, z_out, x, r) do {                    \
+  const struct floatbits *_x = (x);                                    \
   unsigned _rc = 0;                                                    \
+  FROBNAN_ENCDECLS;                                                    \
                                                                        \
   /* See if the native format is one that we recognize. */             \
   switch (TY##_FORMAT&(FLTFMT_ORGMASK | FLTFMT_TYPEMASK)) {            \
@@ -1215,8 +1255,8 @@ unsigned fltfmt_decidblext80(struct floatbits *z_out, uint16 se, kludge64 m)
       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;                    \
@@ -1227,8 +1267,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);                    \
-      (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]);           \
@@ -1247,8 +1288,8 @@ unsigned fltfmt_decidblext80(struct floatbits *z_out, uint16 se, kludge64 m)
       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]);         \
@@ -1266,8 +1307,9 @@ unsigned fltfmt_decidblext80(struct floatbits *z_out, uint16 se, kludge64 m)
       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]);                                     \
@@ -1284,7 +1326,6 @@ unsigned fltfmt_decidblext80(struct floatbits *z_out, uint16 se, kludge64 m)
     default: {                                                         \
       /* We must do this the hard way. */                              \
                                                                        \
-      const struct floatbits *_x = (x);                                        \
       ty _z;                                                           \
       unsigned _i;                                                     \
       ENC_ROUND_DECLS;                                                 \
@@ -1466,7 +1507,16 @@ unsigned fltfmt_encldbl(long double *z_out,
    } while (0)
 #endif
 
+#ifdef FROB_NANS
+#  define FROBNAN_DEC do {                                             \
+     if (_z->f&FLTF_NANMASK) _z->f ^= FLTF_NANMASK;                    \
+   } while (0)
+#else
+#  define FROBNAN_DEC do ; while (0)
+#endif
+
 #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)) {            \
@@ -1480,8 +1530,7 @@ unsigned fltfmt_encldbl(long double *z_out,
        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: {                                            \
@@ -1500,8 +1549,7 @@ unsigned fltfmt_encldbl(long double *z_out,
          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: {                                           \
@@ -1519,8 +1567,7 @@ unsigned fltfmt_encldbl(long double *z_out,
          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: {                                           \
@@ -1538,12 +1585,10 @@ unsigned fltfmt_encldbl(long double *z_out,
          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: {                                                         \
-      struct floatbits *_z = (z_out);                                  \
       ty _x = (x), _y;                                                 \
       unsigned _i, _n, _f = 0;                                         \
       uint32 _t;                                                       \