summaryrefslogtreecommitdiff
path: root/lib/libm/expm1f.c
blob: 7cc13b914c7249c64dfb08b75eca1f80b47896a2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/* origin: FreeBSD /usr/src/lib/msun/src/s_expm1f.c */
/*
 * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
 */
/*
 * ====================================================
 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
 *
 * Developed at SunPro, a Sun Microsystems, Inc. business.
 * Permission to use, copy, modify, and distribute this
 * software is freely granted, provided that this notice
 * is preserved.
 * ====================================================
 */

#include "libm.h"

static const float ln2_hi = 6.9313812256e-01, /* 0x3f317180 */
	ln2_lo = 9.0580006145e-06,	      /* 0x3717f7d1 */
	invln2 = 1.4426950216e+00,	      /* 0x3fb8aa3b */
	/*
	 * Domain [-0.34568, 0.34568], range ~[-6.694e-10, 6.696e-10]:
	 * |6 / x * (1 + 2 * (1 / (exp(x) - 1) - 1 / x)) - q(x)| < 2**-30.04
	 * Scaled coefficients: Qn_here = 2**n * Qn_for_q (see s_expm1.c):
	 */
	Q1 = -3.3333212137e-2, /* -0x888868.0p-28 */
	Q2 = 1.5807170421e-3;  /*  0xcf3010.0p-33 */

float expm1f(float x)
{
	float_t y, hi, lo, c, t, e, hxs, hfx, r1, twopk;
	union {
		float f;
		uint32_t i;
	} u = { x };
	uint32_t hx = u.i & 0x7fffffff;
	int k, sign = u.i >> 31;

	/* filter out huge and non-finite argument */
	if (hx >= 0x4195b844) {	     /* if |x|>=27*ln2 */
		if (hx > 0x7f800000) /* NaN */
			return x;
		if (sign)
			return -1;
		if (hx > 0x42b17217) { /* x > log(FLT_MAX) */
			x *= 0x1p127f;
			return x;
		}
	}

	/* argument reduction */
	if (hx > 0x3eb17218) {	       /* if  |x| > 0.5 ln2 */
		if (hx < 0x3F851592) { /* and |x| < 1.5 ln2 */
			if (!sign) {
				hi = x - ln2_hi;
				lo = ln2_lo;
				k = 1;
			} else {
				hi = x + ln2_hi;
				lo = -ln2_lo;
				k = -1;
			}
		} else {
			k = invln2 * x + (sign ? -0.5f : 0.5f);
			t = k;
			hi = x - t * ln2_hi; /* t*ln2_hi is exact here */
			lo = t * ln2_lo;
		}
		x = hi - lo;
		c = (hi - x) - lo;
	} else if (hx < 0x33000000) { /* when |x|<2**-25, return x */
		if (hx < 0x00800000)
			FORCE_EVAL(x * x);
		return x;
	} else
		k = 0;

	/* x is now in primary range */
	hfx = 0.5f * x;
	hxs = x * hfx;
	r1 = 1.0f + hxs * (Q1 + hxs * Q2);
	t = 3.0f - r1 * hfx;
	e = hxs * ((r1 - t) / (6.0f - x * t));
	if (k == 0) /* c is 0 */
		return x - (x * e - hxs);
	e = x * (e - c) - c;
	e -= hxs;
	/* exp(x) ~ 2^k (x_reduced - e + 1) */
	if (k == -1)
		return 0.5f * (x - e) - 0.5f;
	if (k == 1) {
		if (x < -0.25f)
			return -2.0f * (e - (x + 0.5f));
		return 1.0f + 2.0f * (x - e);
	}
	u.i = (0x7f + k) << 23; /* 2^k */
	twopk = u.f;
	if (k < 0 || k > 56) { /* suffice to return exp(x)-1 */
		y = x - e + 1.0f;
		if (k == 128)
			y = y * 2.0f * 0x1p127f;
		else
			y = y * twopk;
		return y - 1.0f;
	}
	u.i = (0x7f - k) << 23; /* 2^-k */
	if (k < 23)
		y = (x - e + (1 - u.f)) * twopk;
	else
		y = (x - (e + u.f) + 1) * twopk;
	return y;
}