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

     1  /* origin: FreeBSD /usr/src/lib/msun/src/e_jnf.c */
     2  /*
     3   * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
     4   */
     5  /*
     6   * ====================================================
     7   * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
     8   *
     9   * Developed at SunPro, a Sun Microsystems, Inc. business.
    10   * Permission to use, copy, modify, and distribute this
    11   * software is freely granted, provided that this notice
    12   * is preserved.
    13   * ====================================================
    14   */
    15  
    16  #define _GNU_SOURCE
    17  #include "libm.h"
    18  
    19  float jnf(int n, float x)
    20  {
    21  	uint32_t ix;
    22  	int nm1, sign, i;
    23  	float a, b, temp;
    24  
    25  	GET_FLOAT_WORD(ix, x);
    26  	sign = ix>>31;
    27  	ix &= 0x7fffffff;
    28  	if (ix > 0x7f800000) /* nan */
    29  		return x;
    30  
    31  	/* J(-n,x) = J(n,-x), use |n|-1 to avoid overflow in -n */
    32  	if (n == 0)
    33  		return j0f(x);
    34  	if (n < 0) {
    35  		nm1 = -(n+1);
    36  		x = -x;
    37  		sign ^= 1;
    38  	} else
    39  		nm1 = n-1;
    40  	if (nm1 == 0)
    41  		return j1f(x);
    42  
    43  	sign &= n;  /* even n: 0, odd n: signbit(x) */
    44  	x = fabsf(x);
    45  	if (ix == 0 || ix == 0x7f800000)  /* if x is 0 or inf */
    46  		b = 0.0f;
    47  	else if (nm1 < x) {
    48  		/* Safe to use J(n+1,x)=2n/x *J(n,x)-J(n-1,x) */
    49  		a = j0f(x);
    50  		b = j1f(x);
    51  		for (i=0; i<nm1; ){
    52  			i++;
    53  			temp = b;
    54  			b = b*(2.0f*i/x) - a;
    55  			a = temp;
    56  		}
    57  	} else {
    58  		if (ix < 0x35800000) { /* x < 2**-20 */
    59  			/* x is tiny, return the first Taylor expansion of J(n,x)
    60  			 * J(n,x) = 1/n!*(x/2)^n  - ...
    61  			 */
    62  			if (nm1 > 8)  /* underflow */
    63  				nm1 = 8;
    64  			temp = 0.5f * x;
    65  			b = temp;
    66  			a = 1.0f;
    67  			for (i=2; i<=nm1+1; i++) {
    68  				a *= (float)i;    /* a = n! */
    69  				b *= temp;        /* b = (x/2)^n */
    70  			}
    71  			b = b/a;
    72  		} else {
    73  			/* use backward recurrence */
    74  			/*                      x      x^2      x^2
    75  			 *  J(n,x)/J(n-1,x) =  ----   ------   ------   .....
    76  			 *                      2n  - 2(n+1) - 2(n+2)
    77  			 *
    78  			 *                      1      1        1
    79  			 *  (for large x)   =  ----  ------   ------   .....
    80  			 *                      2n   2(n+1)   2(n+2)
    81  			 *                      -- - ------ - ------ -
    82  			 *                       x     x         x
    83  			 *
    84  			 * Let w = 2n/x and h=2/x, then the above quotient
    85  			 * is equal to the continued fraction:
    86  			 *                  1
    87  			 *      = -----------------------
    88  			 *                     1
    89  			 *         w - -----------------
    90  			 *                        1
    91  			 *              w+h - ---------
    92  			 *                     w+2h - ...
    93  			 *
    94  			 * To determine how many terms needed, let
    95  			 * Q(0) = w, Q(1) = w(w+h) - 1,
    96  			 * Q(k) = (w+k*h)*Q(k-1) - Q(k-2),
    97  			 * When Q(k) > 1e4      good for single
    98  			 * When Q(k) > 1e9      good for double
    99  			 * When Q(k) > 1e17     good for quadruple
   100  			 */
   101  			/* determine k */
   102  			float t,q0,q1,w,h,z,tmp,nf;
   103  			int k;
   104  
   105  			nf = nm1+1.0f;
   106  			w = 2*nf/x;
   107  			h = 2/x;
   108  			z = w+h;
   109  			q0 = w;
   110  			q1 = w*z - 1.0f;
   111  			k = 1;
   112  			while (q1 < 1.0e4f) {
   113  				k += 1;
   114  				z += h;
   115  				tmp = z*q1 - q0;
   116  				q0 = q1;
   117  				q1 = tmp;
   118  			}
   119  			for (t=0.0f, i=k; i>=0; i--)
   120  				t = 1.0f/(2*(i+nf)/x-t);
   121  			a = t;
   122  			b = 1.0f;
   123  			/*  estimate log((2/x)^n*n!) = n*log(2/x)+n*ln(n)
   124  			 *  Hence, if n*(log(2n/x)) > ...
   125  			 *  single 8.8722839355e+01
   126  			 *  double 7.09782712893383973096e+02
   127  			 *  long double 1.1356523406294143949491931077970765006170e+04
   128  			 *  then recurrent value may overflow and the result is
   129  			 *  likely underflow to zero
   130  			 */
   131  			tmp = nf*logf(fabsf(w));
   132  			if (tmp < 88.721679688f) {
   133  				for (i=nm1; i>0; i--) {
   134  					temp = b;
   135  					b = 2.0f*i*b/x - a;
   136  					a = temp;
   137  				}
   138  			} else {
   139  				for (i=nm1; i>0; i--){
   140  					temp = b;
   141  					b = 2.0f*i*b/x - a;
   142  					a = temp;
   143  					/* scale b to avoid spurious overflow */
   144  					if (b > 0x1p60f) {
   145  						a /= b;
   146  						t /= b;
   147  						b = 1.0f;
   148  					}
   149  				}
   150  			}
   151  			z = j0f(x);
   152  			w = j1f(x);
   153  			if (fabsf(z) >= fabsf(w))
   154  				b = t*z/b;
   155  			else
   156  				b = t*w/a;
   157  		}
   158  	}
   159  	return sign ? -b : b;
   160  }
   161  
   162  float ynf(int n, float x)
   163  {
   164  	uint32_t ix, ib;
   165  	int nm1, sign, i;
   166  	float a, b, temp;
   167  
   168  	GET_FLOAT_WORD(ix, x);
   169  	sign = ix>>31;
   170  	ix &= 0x7fffffff;
   171  	if (ix > 0x7f800000) /* nan */
   172  		return x;
   173  	if (sign && ix != 0) /* x < 0 */
   174  		return 0/0.0f;
   175  	if (ix == 0x7f800000)
   176  		return 0.0f;
   177  
   178  	if (n == 0)
   179  		return y0f(x);
   180  	if (n < 0) {
   181  		nm1 = -(n+1);
   182  		sign = n&1;
   183  	} else {
   184  		nm1 = n-1;
   185  		sign = 0;
   186  	}
   187  	if (nm1 == 0)
   188  		return sign ? -y1f(x) : y1f(x);
   189  
   190  	a = y0f(x);
   191  	b = y1f(x);
   192  	/* quit if b is -inf */
   193  	GET_FLOAT_WORD(ib,b);
   194  	for (i = 0; i < nm1 && ib != 0xff800000; ) {
   195  		i++;
   196  		temp = b;
   197  		b = (2.0f*i/x)*b - a;
   198  		GET_FLOAT_WORD(ib, b);
   199  		a = temp;
   200  	}
   201  	return sign ? -b : b;
   202  }