github.com/afumu/libc@v0.0.6/musl/src/math/lrint.c (about)

     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