11dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project/* @(#)e_fmod.c 1.3 95/01/18 */
21dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project/*-
31dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * ====================================================
41dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
51dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project *
61dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * Developed at SunSoft, a Sun Microsystems, Inc. business.
71dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * Permission to use, copy, modify, and distribute this
81dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * software is freely granted, provided that this notice
91dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * is preserved.
101dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * ====================================================
111dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project */
121dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
131dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#include <sys/cdefs.h>
14a0ee07829a9ba7e99ef68e8c12551301cc797f0fElliott Hughes__FBSDID("$FreeBSD$");
151dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
161dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#include "math.h"
171dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project#include "math_private.h"
181dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
191dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectstatic const float Zero[] = {0.0, -0.0,};
201dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
211dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project/*
221dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * Return the IEEE remainder and set *quo to the last n bits of the
231dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * quotient, rounded to the nearest integer.  We choose n=31 because
241dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * we wind up computing all the integer bits of the quotient anyway as
251dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * a side-effect of computing the remainder by the shift and subtract
261dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * method.  In practice, this is far more bits than are needed to use
271dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project * remquo in reduction algorithms.
281dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project */
291dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectfloat
301dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectremquof(float x, float y, int *quo)
311dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project{
321dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	int32_t n,hx,hy,hz,ix,iy,sx,i;
331dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	u_int32_t q,sxy;
341dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
351dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	GET_FLOAT_WORD(hx,x);
361dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	GET_FLOAT_WORD(hy,y);
371dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	sxy = (hx ^ hy) & 0x80000000;
381dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	sx = hx&0x80000000;		/* sign of x */
391dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	hx ^=sx;		/* |x| */
401dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	hy &= 0x7fffffff;	/* |y| */
411dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
421dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    /* purge off exception values */
431dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	if(hy==0||hx>=0x7f800000||hy>0x7f800000) /* y=0,NaN;or x not finite */
441dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    return (x*y)/(x*y);
451dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	if(hx<hy) {
461dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    q = 0;
471dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    goto fixup;	/* |x|<|y| return x or x-y */
481dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	} else if(hx==hy) {
49a0ee07829a9ba7e99ef68e8c12551301cc797f0fElliott Hughes	    *quo = (sxy ? -1 : 1);
501dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    return Zero[(u_int32_t)sx>>31];	/* |x|=|y| return x*0*/
511dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	}
521dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
531dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    /* determine ix = ilogb(x) */
541dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	if(hx<0x00800000) {	/* subnormal x */
551dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    for (ix = -126,i=(hx<<8); i>0; i<<=1) ix -=1;
561dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	} else ix = (hx>>23)-127;
571dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
581dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    /* determine iy = ilogb(y) */
591dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	if(hy<0x00800000) {	/* subnormal y */
601dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    for (iy = -126,i=(hy<<8); i>0; i<<=1) iy -=1;
611dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	} else iy = (hy>>23)-127;
621dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
631dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    /* set up {hx,lx}, {hy,ly} and align y to x */
641dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	if(ix >= -126)
651dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    hx = 0x00800000|(0x007fffff&hx);
661dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	else {		/* subnormal x, shift x to normal */
671dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    n = -126-ix;
681dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    hx <<= n;
691dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	}
701dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	if(iy >= -126)
711dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    hy = 0x00800000|(0x007fffff&hy);
721dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	else {		/* subnormal y, shift y to normal */
731dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    n = -126-iy;
741dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    hy <<= n;
751dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	}
761dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
771dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    /* fix point fmod */
781dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	n = ix - iy;
791dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	q = 0;
801dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	while(n--) {
811dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    hz=hx-hy;
821dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    if(hz<0) hx = hx << 1;
831dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    else {hx = hz << 1; q++;}
841dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    q <<= 1;
851dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	}
861dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	hz=hx-hy;
871dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	if(hz>=0) {hx=hz;q++;}
881dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project
891dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project    /* convert back to floating value and restore the sign */
901dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	if(hx==0) {				/* return sign(x)*0 */
91a0ee07829a9ba7e99ef68e8c12551301cc797f0fElliott Hughes	    q &= 0x7fffffff;
92a0ee07829a9ba7e99ef68e8c12551301cc797f0fElliott Hughes	    *quo = (sxy ? -q : q);
931dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    return Zero[(u_int32_t)sx>>31];
941dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	}
951dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	while(hx<0x00800000) {		/* normalize x */
961dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    hx <<= 1;
971dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    iy -= 1;
981dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	}
991dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	if(iy>= -126) {		/* normalize output */
1001dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    hx = ((hx-0x00800000)|((iy+127)<<23));
1011dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	} else {		/* subnormal output */
1021dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    n = -126 - iy;
1031dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    hx >>= n;
1041dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	}
1051dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Projectfixup:
1061dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	SET_FLOAT_WORD(x,hx);
1071dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	y = fabsf(y);
1081dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	if (y < 0x1p-125f) {
1091dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    if (x+x>y || (x+x==y && (q & 1))) {
1101dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project		q++;
1111dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project		x-=y;
1121dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    }
1131dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	} else if (x>0.5f*y || (x==0.5f*y && (q & 1))) {
1141dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    q++;
1151dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	    x-=y;
1161dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	}
1171dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	GET_FLOAT_WORD(hx,x);
1181dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	SET_FLOAT_WORD(x,hx^sx);
1191dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	q &= 0x7fffffff;
1201dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	*quo = (sxy ? -q : q);
1211dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project	return x;
1221dc9e472e19acfe6dc7f41e429236e7eef7ceda1The Android Open Source Project}
123