summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--numpy/core/src/npymath/ieee754.c.src124
-rw-r--r--numpy/core/src/private/npy_fpmath.h3
2 files changed, 126 insertions, 1 deletions
diff --git a/numpy/core/src/npymath/ieee754.c.src b/numpy/core/src/npymath/ieee754.c.src
index ded0937f5..8df903b2b 100644
--- a/numpy/core/src/npymath/ieee754.c.src
+++ b/numpy/core/src/npymath/ieee754.c.src
@@ -126,6 +126,129 @@ float _nextf(float x, int p)
return x;
}
+#ifdef HAVE_LDOUBLE_DOUBLE_DOUBLE_BE
+
+/*
+ * FIXME: this is ugly and untested. The asm part only works with gcc, and we
+ * should consolidate the GET_LDOUBLE* / SET_LDOUBLE macros
+ */
+#define math_opt_barrier(x) \
+ ({ __typeof (x) __x = x; __asm ("" : "+m" (__x)); __x; })
+#define math_force_eval(x) __asm __volatile ("" : : "m" (x))
+
+/* only works for big endian */
+typedef union
+{
+ npy_longdouble value;
+ struct
+ {
+ npy_uint64 msw;
+ npy_uint64 lsw;
+ } parts64;
+ struct
+ {
+ npy_uint32 w0, w1, w2, w3;
+ } parts32;
+} ieee854_long_double_shape_type;
+
+/* Get two 64 bit ints from a long double. */
+
+#define GET_LDOUBLE_WORDS64(ix0,ix1,d) \
+do { \
+ ieee854_long_double_shape_type qw_u; \
+ qw_u.value = (d); \
+ (ix0) = qw_u.parts64.msw; \
+ (ix1) = qw_u.parts64.lsw; \
+} while (0)
+
+/* Set a long double from two 64 bit ints. */
+
+#define SET_LDOUBLE_WORDS64(d,ix0,ix1) \
+do { \
+ ieee854_long_double_shape_type qw_u; \
+ qw_u.parts64.msw = (ix0); \
+ qw_u.parts64.lsw = (ix1); \
+ (d) = qw_u.value; \
+} while (0)
+
+npy_longdouble _nextl(npy_longdouble x, int p)
+{
+ npy_int64 hx,ihx,ilx;
+ npy_uint64 lx;
+
+ GET_LDOUBLE_WORDS64(hx, lx, x);
+ ihx = hx & 0x7fffffffffffffffLL; /* |hx| */
+ ilx = lx & 0x7fffffffffffffffLL; /* |lx| */
+
+ if(((ihx & 0x7ff0000000000000LL)==0x7ff0000000000000LL)&&
+ ((ihx & 0x000fffffffffffffLL)!=0)) {
+ return x; /* signal the nan */
+ }
+ if(ihx == 0 && ilx == 0) { /* x == 0 */
+ npy_longdouble u;
+ SET_LDOUBLE_WORDS64(x, p, 0ULL);/* return +-minsubnormal */
+ u = x * x;
+ if (u == x) {
+ return u;
+ } else {
+ return x; /* raise underflow flag */
+ }
+ }
+
+ npy_longdouble u;
+ if(p < 0) { /* p < 0, x -= ulp */
+ if((hx==0xffefffffffffffffLL)&&(lx==0xfc8ffffffffffffeLL))
+ return x+x; /* overflow, return -inf */
+ if (hx >= 0x7ff0000000000000LL) {
+ SET_LDOUBLE_WORDS64(u,0x7fefffffffffffffLL,0x7c8ffffffffffffeLL);
+ return u;
+ }
+ if(ihx <= 0x0360000000000000LL) { /* x <= LDBL_MIN */
+ u = math_opt_barrier (x);
+ x -= __LDBL_DENORM_MIN__;
+ if (ihx < 0x0360000000000000LL
+ || (hx > 0 && (npy_int64) lx <= 0)
+ || (hx < 0 && (npy_int64) lx > 1)) {
+ u = u * u;
+ math_force_eval (u); /* raise underflow flag */
+ }
+ return x;
+ }
+ if (ihx < 0x06a0000000000000LL) { /* ulp will denormal */
+ SET_LDOUBLE_WORDS64(u,(hx&0x7ff0000000000000LL),0ULL);
+ u *= 0x1.0000000000000p-105L;
+ } else
+ SET_LDOUBLE_WORDS64(u,(hx&0x7ff0000000000000LL)-0x0690000000000000LL,0ULL);
+ return x - u;
+ } else { /* p >= 0, x += ulp */
+ if((hx==0x7fefffffffffffffLL)&&(lx==0x7c8ffffffffffffeLL))
+ return x+x; /* overflow, return +inf */
+ if ((npy_uint64) hx >= 0xfff0000000000000ULL) {
+ SET_LDOUBLE_WORDS64(u,0xffefffffffffffffLL,0xfc8ffffffffffffeLL);
+ return u;
+ }
+ if(ihx <= 0x0360000000000000LL) { /* x <= LDBL_MIN */
+ u = math_opt_barrier (x);
+ x += __LDBL_DENORM_MIN__;
+ if (ihx < 0x0360000000000000LL
+ || (hx > 0 && (npy_int64) lx < 0 && lx != 0x8000000000000001LL)
+ || (hx < 0 && (npy_int64) lx >= 0)) {
+ u = u * u;
+ math_force_eval (u); /* raise underflow flag */
+ }
+ if (x == 0.0L) /* handle negative __LDBL_DENORM_MIN__ case */
+ x = -0.0L;
+ return x;
+ }
+ if (ihx < 0x06a0000000000000LL) { /* ulp will denormal */
+ SET_LDOUBLE_WORDS64(u,(hx&0x7ff0000000000000LL),0ULL);
+ u *= 0x1.0000000000000p-105L;
+ } else
+ SET_LDOUBLE_WORDS64(u,(hx&0x7ff0000000000000LL)-0x0690000000000000LL,0ULL);
+ return x + u;
+ }
+}
+#else
npy_longdouble _nextl(npy_longdouble x, int p)
{
volatile npy_longdouble t;
@@ -188,6 +311,7 @@ npy_longdouble _nextl(npy_longdouble x, int p)
return ux.e;
}
+#endif
/*
* nextafter code taken from BSD math lib, the code contains the following
diff --git a/numpy/core/src/private/npy_fpmath.h b/numpy/core/src/private/npy_fpmath.h
index 4b45a12dc..92338e4c7 100644
--- a/numpy/core/src/private/npy_fpmath.h
+++ b/numpy/core/src/private/npy_fpmath.h
@@ -39,7 +39,8 @@
defined(HAVE_LDOUBLE_IEEE_DOUBLE_BE) || \
defined(HAVE_LDOUBLE_IEEE_DOUBLE_16_BYTES_BE) || \
defined(HAVE_LDOUBLE_INTEL_EXTENDED_16_BYTES_LE) || \
- defined(HAVE_LDOUBLE_INTEL_EXTENDED_12_BYTES_LE))
+ defined(HAVE_LDOUBLE_INTEL_EXTENDED_12_BYTES_LE) || \
+ defined(HAVE_LDOUBLE_DOUBLE_DOUBLE_BE))
#error No long double representation defined
#endif