import numpy as np from numpy.testing import assert_ cdef extern from "_round.h": double add_round_up(double, double) nogil double add_round_down(double, double) nogil int fesetround(int) nogil int fegetround() nogil int FE_UPWARD int FE_DOWNWARD cdef extern from "numpy/npy_math.h": int npy_isnan(double) nogil def have_fenv(): old_round = fegetround() have_getround = True if old_round >= 0 else False if not have_getround: return False have_setround = True try: if fesetround(FE_UPWARD) != 0: have_setround = False if fesetround(FE_DOWNWARD) != 0: have_setround = False finally: fesetround(old_round) return have_setround def random_double(size): # This code is a little hacky to work around some issues: # - randint doesn't have a dtype keyword until 1.11 # and the default type of randint is np.int_ # - Something like # >>> low = np.iinfo(np.int_).min # >>> high = np.iinfo(np.int_).max + 1 # >>> np.random.randint(low=low, high=high) # fails in NumPy 1.10.4 (note that the 'high' value in randint # is exclusive); this is fixed in 1.11. x = np.random.randint(low=0, high=2**16, size=4*size) return x.astype(np.uint16).view(np.float64) def test_add_round(size, mode): cdef: int i, old_round, status double[:] sample1 = random_double(size) double[:] sample2 = random_double(size) double res, std nfail = 0 msg = [] for i in range(size): old_round = fegetround() if old_round < 0: raise RuntimeError("Couldn't get rounding mode") try: if mode == 'up': res = add_round_up(sample1[i], sample2[i]) status = fesetround(FE_UPWARD) elif mode == 'down': res = add_round_down(sample1[i], sample2[i]) status = fesetround(FE_DOWNWARD) else: raise ValueError("Invalid rounding mode") if status != 0: raise RuntimeError("Failed to set rounding mode") std = sample1[i] + sample2[i] finally: fesetround(old_round) if npy_isnan(res) and npy_isnan(std): continue if res != std: nfail += 1 msg.append("{:.21g} + {:.21g} = {:.21g} != {:.21g}" .format(sample1[i], sample2[i], std, res)) if nfail: s = "{}/{} failures with mode {}.".format(nfail, size, mode) msg = [s] + msg assert_(False, "\n".join(msg))