xref: /openbsd-src/lib/libc/gen/ldexp.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: ldexp.c,v 1.1 2009/04/19 16:42:06 martynas Exp $	*/
2 /* @(#)s_scalbn.c 5.1 93/09/24 */
3 /* @(#)fdlibm.h 5.1 93/09/24 */
4 /*
5  * ====================================================
6  * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
7  *
8  * Developed at SunPro, a Sun Microsystems, Inc. business.
9  * Permission to use, copy, modify, and distribute this
10  * software is freely granted, provided that this notice
11  * is preserved.
12  * ====================================================
13  */
14 
15 #if 0
16 __FBSDID("$FreeBSD: src/lib/libc/gen/ldexp.c,v 1.1 2005/01/22 06:03:40 das Exp $");
17 #endif
18 
19 #include <sys/types.h>
20 #include <sys/cdefs.h>
21 #include <machine/endian.h>
22 #include <float.h>
23 #include <math.h>
24 
25 /* Bit fiddling routines copied from msun/src/math_private.h,v 1.15 */
26 
27 #if BYTE_ORDER == BIG_ENDIAN
28 
29 typedef union
30 {
31   double value;
32   struct
33   {
34     u_int32_t msw;
35     u_int32_t lsw;
36   } parts;
37 } ieee_double_shape_type;
38 
39 #endif
40 
41 #if BYTE_ORDER == LITTLE_ENDIAN
42 
43 typedef union
44 {
45   double value;
46   struct
47   {
48     u_int32_t lsw;
49     u_int32_t msw;
50   } parts;
51 } ieee_double_shape_type;
52 
53 #endif
54 
55 /* Get two 32 bit ints from a double.  */
56 
57 #define EXTRACT_WORDS(ix0,ix1,d)				\
58 do {								\
59   ieee_double_shape_type ew_u;					\
60   ew_u.value = (d);						\
61   (ix0) = ew_u.parts.msw;					\
62   (ix1) = ew_u.parts.lsw;					\
63 } while (0)
64 
65 /* Get the more significant 32 bit int from a double.  */
66 
67 #define GET_HIGH_WORD(i,d)					\
68 do {								\
69   ieee_double_shape_type gh_u;					\
70   gh_u.value = (d);						\
71   (i) = gh_u.parts.msw;						\
72 } while (0)
73 
74 /* Set the more significant 32 bits of a double from an int.  */
75 
76 #define SET_HIGH_WORD(d,v)					\
77 do {								\
78   ieee_double_shape_type sh_u;					\
79   sh_u.value = (d);						\
80   sh_u.parts.msw = (v);						\
81   (d) = sh_u.value;						\
82 } while (0)
83 
84 
85 static const double
86 two54   =  1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */
87 twom54  =  5.55111512312578270212e-17, /* 0x3C900000, 0x00000000 */
88 huge   = 1.0e+300,
89 tiny   = 1.0e-300;
90 
91 static double
92 _copysign(double x, double y)
93 {
94 	u_int32_t hx,hy;
95 	GET_HIGH_WORD(hx,x);
96 	GET_HIGH_WORD(hy,y);
97 	SET_HIGH_WORD(x,(hx&0x7fffffff)|(hy&0x80000000));
98 	return x;
99 }
100 
101 double
102 ldexp(double x, int n)
103 {
104 	int32_t k,hx,lx;
105 	EXTRACT_WORDS(hx,lx,x);
106         k = (hx&0x7ff00000)>>20;		/* extract exponent */
107         if (k==0) {				/* 0 or subnormal x */
108             if ((lx|(hx&0x7fffffff))==0) return x; /* +-0 */
109 	    x *= two54;
110 	    GET_HIGH_WORD(hx,x);
111 	    k = ((hx&0x7ff00000)>>20) - 54;
112             if (n< -50000) return tiny*x; 	/*underflow*/
113 	    }
114         if (k==0x7ff) return x+x;		/* NaN or Inf */
115         k = k+n;
116         if (k >  0x7fe) return huge*_copysign(huge,x); /* overflow  */
117         if (k > 0) 				/* normal result */
118 	    {SET_HIGH_WORD(x,(hx&0x800fffff)|(k<<20)); return x;}
119         if (k <= -54) {
120             if (n > 50000) 	/* in case integer overflow in n+k */
121 		return huge*_copysign(huge,x);	/*overflow*/
122 	    else return tiny*_copysign(tiny,x); 	/*underflow*/
123 	}
124         k += 54;				/* subnormal result */
125 	SET_HIGH_WORD(x,(hx&0x800fffff)|(k<<20));
126         return x*twom54;
127 }
128 
129 #if LDBL_MANT_DIG == 53
130 #ifdef __weak_alias
131 __weak_alias(ldexpl, ldexp);
132 #endif /* __weak_alias */
133 #endif /* LDBL_MANT_DIG == 53 */
134