lrint.c raw

   1  #include <limits.h>
   2  #include <fenv.h>
   3  #include <math.h>
   4  #include "libm.h"
   5  
   6  /*
   7  If the result cannot be represented (overflow, nan), then
   8  lrint raises the invalid exception.
   9  
  10  Otherwise if the input was not an integer then the inexact
  11  exception is raised.
  12  
  13  C99 is a bit vague about whether inexact exception is
  14  allowed to be raised when invalid is raised.
  15  (F.9 explicitly allows spurious inexact exceptions, F.9.6.5
  16  does not make it clear if that rule applies to lrint, but
  17  IEEE 754r 7.8 seems to forbid spurious inexact exception in
  18  the ineger conversion functions)
  19  
  20  So we try to make sure that no spurious inexact exception is
  21  raised in case of an overflow.
  22  
  23  If the bit size of long > precision of double, then there
  24  cannot be inexact rounding in case the result overflows,
  25  otherwise LONG_MAX and LONG_MIN can be represented exactly
  26  as a double.
  27  */
  28  
  29  #if LONG_MAX < 1U<<53 && defined(FE_INEXACT)
  30  #include <float.h>
  31  #include <stdint.h>
  32  #if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1
  33  #define EPS DBL_EPSILON
  34  #elif FLT_EVAL_METHOD==2
  35  #define EPS LDBL_EPSILON
  36  #endif
  37  #ifdef __GNUC__
  38  /* avoid stack frame in lrint */
  39  __attribute__((noinline))
  40  #endif
  41  static long lrint_slow(double x)
  42  {
  43  	#pragma STDC FENV_ACCESS ON
  44  	int e;
  45  
  46  	e = fetestexcept(FE_INEXACT);
  47  	x = rint(x);
  48  	if (!e && (x > LONG_MAX || x < LONG_MIN))
  49  		feclearexcept(FE_INEXACT);
  50  	/* conversion */
  51  	return x;
  52  }
  53  
  54  long lrint(double x)
  55  {
  56  	uint32_t abstop = asuint64(x)>>32 & 0x7fffffff;
  57  	uint64_t sign = asuint64(x) & (1ULL << 63);
  58  
  59  	if (abstop < 0x41dfffff) {
  60  		/* |x| < 0x7ffffc00, no overflow */
  61  		double_t toint = asdouble(asuint64(1/EPS) | sign);
  62  		double_t y = x + toint - toint;
  63  		return (long)y;
  64  	}
  65  	return lrint_slow(x);
  66  }
  67  #else
  68  long lrint(double x)
  69  {
  70  	return rint(x);
  71  }
  72  #endif
  73