xref: /plan9/sys/src/libc/port/frexp.c (revision f1b2ee28c1dcb21e637a95dfec1fecc82992b275)
180ee5cbfSDavid du Colombier #include <u.h>
280ee5cbfSDavid du Colombier #include <libc.h>
380ee5cbfSDavid du Colombier 
480ee5cbfSDavid du Colombier /*
580ee5cbfSDavid du Colombier  * this is big/little endian non-portable
680ee5cbfSDavid du Colombier  * it gets the endian from the FPdbleword
780ee5cbfSDavid du Colombier  * union in u.h.
880ee5cbfSDavid du Colombier  */
980ee5cbfSDavid du Colombier #define	MASK	0x7ffL
1080ee5cbfSDavid du Colombier #define	SHIFT	20
1180ee5cbfSDavid du Colombier #define	BIAS	1022L
12eaba85aaSDavid du Colombier #define	SIG	52
1380ee5cbfSDavid du Colombier 
1480ee5cbfSDavid du Colombier double
frexp(double d,int * ep)1580ee5cbfSDavid du Colombier frexp(double d, int *ep)
1680ee5cbfSDavid du Colombier {
17eaba85aaSDavid du Colombier 	FPdbleword x, a;
1880ee5cbfSDavid du Colombier 
1980ee5cbfSDavid du Colombier 	*ep = 0;
20eaba85aaSDavid du Colombier 	/* order matters: only isNaN can operate on NaN */
21eaba85aaSDavid du Colombier 	if(isNaN(d) || isInf(d, 0) || d == 0)
22eaba85aaSDavid du Colombier 		return d;
2380ee5cbfSDavid du Colombier 	x.x = d;
24eaba85aaSDavid du Colombier 	a.x = fabs(d);
25eaba85aaSDavid du Colombier 	if((a.hi >> SHIFT) == 0){	/* normalize subnormal numbers */
26eaba85aaSDavid du Colombier 		x.x = (double)(1ULL<<SIG) * d;
27eaba85aaSDavid du Colombier 		*ep = -SIG;
28eaba85aaSDavid du Colombier 	}
29eaba85aaSDavid du Colombier 	*ep += ((x.hi >> SHIFT) & MASK) - BIAS;
3080ee5cbfSDavid du Colombier 	x.hi &= ~(MASK << SHIFT);
3180ee5cbfSDavid du Colombier 	x.hi |= BIAS << SHIFT;
3280ee5cbfSDavid du Colombier 	return x.x;
3380ee5cbfSDavid du Colombier }
3480ee5cbfSDavid du Colombier 
3580ee5cbfSDavid du Colombier double
ldexp(double d,int deltae)369a747e4fSDavid du Colombier ldexp(double d, int deltae)
3780ee5cbfSDavid du Colombier {
389a747e4fSDavid du Colombier 	int e, bits;
3980ee5cbfSDavid du Colombier 	FPdbleword x;
409a747e4fSDavid du Colombier 	ulong z;
4180ee5cbfSDavid du Colombier 
4280ee5cbfSDavid du Colombier 	if(d == 0)
4380ee5cbfSDavid du Colombier 		return 0;
4480ee5cbfSDavid du Colombier 	x.x = d;
459a747e4fSDavid du Colombier 	e = (x.hi >> SHIFT) & MASK;
469a747e4fSDavid du Colombier 	if(deltae >= 0 || e+deltae >= 1){	/* no underflow */
479a747e4fSDavid du Colombier 		e += deltae;
4880ee5cbfSDavid du Colombier 		if(e >= MASK){		/* overflow */
4980ee5cbfSDavid du Colombier 			if(d < 0)
5080ee5cbfSDavid du Colombier 				return Inf(-1);
5180ee5cbfSDavid du Colombier 			return Inf(1);
5280ee5cbfSDavid du Colombier 		}
539a747e4fSDavid du Colombier 	}else{	/* underflow gracefully */
549a747e4fSDavid du Colombier 		deltae = -deltae;
559a747e4fSDavid du Colombier 		/* need to shift d right deltae */
569a747e4fSDavid du Colombier 		if(e > 1){		/* shift e-1 by exponent manipulation */
579a747e4fSDavid du Colombier 			deltae -= e-1;
589a747e4fSDavid du Colombier 			e = 1;
599a747e4fSDavid du Colombier 		}
609a747e4fSDavid du Colombier 		if(deltae > 0 && e==1){	/* shift 1 by switch from 1.fff to 0.1ff */
619a747e4fSDavid du Colombier 			deltae--;
629a747e4fSDavid du Colombier 			e = 0;
639a747e4fSDavid du Colombier 			x.lo >>= 1;
649a747e4fSDavid du Colombier 			x.lo |= (x.hi&1)<<31;
659a747e4fSDavid du Colombier 			z = x.hi & ((1<<SHIFT)-1);
669a747e4fSDavid du Colombier 			x.hi &= ~((1<<SHIFT)-1);
679a747e4fSDavid du Colombier 			x.hi |= (1<<(SHIFT-1)) | (z>>1);
689a747e4fSDavid du Colombier 		}
699a747e4fSDavid du Colombier 		while(deltae > 0){		/* shift bits down */
709a747e4fSDavid du Colombier 			bits = deltae;
719a747e4fSDavid du Colombier 			if(bits > SHIFT)
729a747e4fSDavid du Colombier 				bits = SHIFT;
739a747e4fSDavid du Colombier 			x.lo >>= bits;
749a747e4fSDavid du Colombier 			x.lo |= (x.hi&((1<<bits)-1)) << (32-bits);
759a747e4fSDavid du Colombier 			z = x.hi & ((1<<SHIFT)-1);
769a747e4fSDavid du Colombier 			x.hi &= ~((1<<SHIFT)-1);
779a747e4fSDavid du Colombier 			x.hi |= z>>bits;
789a747e4fSDavid du Colombier 			deltae -= bits;
799a747e4fSDavid du Colombier 		}
809a747e4fSDavid du Colombier 	}
8180ee5cbfSDavid du Colombier 	x.hi &= ~(MASK << SHIFT);
8280ee5cbfSDavid du Colombier 	x.hi |= (long)e << SHIFT;
8380ee5cbfSDavid du Colombier 	return x.x;
8480ee5cbfSDavid du Colombier }
8580ee5cbfSDavid du Colombier 
8680ee5cbfSDavid du Colombier double
modf(double d,double * ip)8780ee5cbfSDavid du Colombier modf(double d, double *ip)
8880ee5cbfSDavid du Colombier {
8980ee5cbfSDavid du Colombier 	FPdbleword x;
9080ee5cbfSDavid du Colombier 	int e;
9180ee5cbfSDavid du Colombier 
92*f1b2ee28SDavid du Colombier 	x.x = d;
93*f1b2ee28SDavid du Colombier 	e = (x.hi >> SHIFT) & MASK;
94*f1b2ee28SDavid du Colombier 	if(e == MASK){
95*f1b2ee28SDavid du Colombier 		*ip = d;
96*f1b2ee28SDavid du Colombier 		if(x.lo != 0 || (x.hi & 0xfffffL) != 0)	/* NaN */
97*f1b2ee28SDavid du Colombier 			return d;
98*f1b2ee28SDavid du Colombier 		/* ±Inf */
99*f1b2ee28SDavid du Colombier 		x.hi &= 0x80000000L;
100*f1b2ee28SDavid du Colombier 		return x.x;
101*f1b2ee28SDavid du Colombier 	}
10280ee5cbfSDavid du Colombier 	if(d < 1) {
10380ee5cbfSDavid du Colombier 		if(d < 0) {
10480ee5cbfSDavid du Colombier 			x.x = modf(-d, ip);
10580ee5cbfSDavid du Colombier 			*ip = -*ip;
10680ee5cbfSDavid du Colombier 			return -x.x;
10780ee5cbfSDavid du Colombier 		}
10880ee5cbfSDavid du Colombier 		*ip = 0;
10980ee5cbfSDavid du Colombier 		return d;
11080ee5cbfSDavid du Colombier 	}
111*f1b2ee28SDavid du Colombier 	e -= BIAS;
11280ee5cbfSDavid du Colombier 	if(e <= SHIFT+1) {
11380ee5cbfSDavid du Colombier 		x.hi &= ~(0x1fffffL >> e);
11480ee5cbfSDavid du Colombier 		x.lo = 0;
11580ee5cbfSDavid du Colombier 	} else
11680ee5cbfSDavid du Colombier 	if(e <= SHIFT+33)
11780ee5cbfSDavid du Colombier 		x.lo &= ~(0x7fffffffL >> (e-SHIFT-2));
11880ee5cbfSDavid du Colombier 	*ip = x.x;
11980ee5cbfSDavid du Colombier 	return d - x.x;
12080ee5cbfSDavid du Colombier }
121