diff options
author | Matt Turner <mattst88@gmail.com> | 2008-11-11 21:27:09 +0000 |
---|---|---|
committer | Matt Turner <mattst88@gmail.com> | 2008-11-11 21:27:09 +0000 |
commit | 91b4edf69e5adf9c40edcaff38b880218b7b0d9d (patch) | |
tree | 85b654192fa2ce167f4899f6f0a4ca14b1acdb13 /emfloat.c |
Initial Import
git-svn-id: svn://mattst88.com/svn/cleanbench/trunk@1 0d43b9a7-5ab2-4d7b-af9d-f64450cef757
Diffstat (limited to 'emfloat.c')
-rw-r--r-- | emfloat.c | 1343 |
1 files changed, 1343 insertions, 0 deletions
diff --git a/emfloat.c b/emfloat.c new file mode 100644 index 0000000..5e73890 --- /dev/null +++ b/emfloat.c @@ -0,0 +1,1343 @@ +/* +** emfloat.c +** Source for emulated floating-point routines. +** BYTEmark (tm) +** BYTE's Native Mode Benchmarks +** Rick Grehan, BYTE Magazine. +** +** Created: +** Last update: 3/95 +** +** DISCLAIMER +** The source, executable, and documentation files that comprise +** the BYTEmark benchmarks are made available on an "as is" basis. +** This means that we at BYTE Magazine have made every reasonable +** effort to verify that the there are no errors in the source and +** executable code. We cannot, however, guarantee that the programs +** are error-free. Consequently, McGraw-HIll and BYTE Magazine make +** no claims in regard to the fitness of the source code, executable +** code, and documentation of the BYTEmark. +** Furthermore, BYTE Magazine, McGraw-Hill, and all employees +** of McGraw-Hill cannot be held responsible for any damages resulting +** from the use of this code or the results obtained from using +** this code. +*/ + + +#include <stdio.h> +#include <string.h> +#include "nmglobal.h" +#include "emfloat.h" + +/* +** Floating-point emulator. +** These routines are only "sort of" IEEE-compliant. All work is +** done using an internal representation. Also, the routines do +** not check for many of the exceptions that might occur. +** Still, the external formats produced are IEEE-compatible, +** with the restriction that they presume a low-endian machine +** (though the endianism will not effect the performance). +** +** Some code here was based on work done by Steve Snelgrove of +** Orem, UT. Other code comes from routines presented in +** the long-ago book: "Microprocessor Programming for +** Computer Hobbyists" by Neill Graham. +*/ + +/************************** +** SetupCPUEmFloatArrays ** +*************************** +** Set up the arrays that will be used in the emulated +** floating-point tests. +** This is done by loading abase and bbase elements with +** random numbers. We use our long-to-floating point +** routine to set them up. +** NOTE: We really don't need the pointer to cbase...cbase +** is overwritten in the benchmark. +*/ +void SetupCPUEmFloatArrays(InternalFPF *abase, + InternalFPF *bbase, + InternalFPF *cbase, + ulong arraysize) +{ +ulong i; +InternalFPF locFPF1,locFPF2; +/* +** Reset random number generator so things repeat. Inserted by Uwe F. Mayer. +*/ +extern int32 randnum(int32 lngval); +randnum((int32)13); + +for(i=0;i<arraysize;i++) +{/* LongToInternalFPF(randwc(50000L),&locFPF1); */ + Int32ToInternalFPF(randwc((int32)50000),&locFPF1); + /* LongToInternalFPF(randwc(50000L)+1L,&locFPF2); */ + Int32ToInternalFPF(randwc((int32)50000)+(int32)1,&locFPF2); + DivideInternalFPF(&locFPF1,&locFPF2,abase+i); + /* LongToInternalFPF(randwc(50000L)+1L,&locFPF2); */ + Int32ToInternalFPF(randwc((int32)50000)+(int32)1,&locFPF2); + DivideInternalFPF(&locFPF1,&locFPF2,bbase+i); +} +return; +} + +/*********************** +** DoEmFloatIteration ** +************************ +** Perform an iteration of the emulated floating-point +** benchmark. Note that "an iteration" can involve multiple +** loops through the benchmark. +*/ +ulong DoEmFloatIteration(InternalFPF *abase, + InternalFPF *bbase, + InternalFPF *cbase, + ulong arraysize, ulong loops) +{ +ulong elapsed; /* For the stopwatch */ +static uchar jtable[16] = {0,0,0,0,1,1,1,1,2,2,2,2,2,3,3,3}; +ulong i; +#ifdef DEBUG +int number_of_loops; +#endif +/* +** Begin timing +*/ +elapsed=StartStopwatch(); +#ifdef DEBUG +number_of_loops=loops-1; /* the index of the first loop we run */ +#endif + +/* +** Each pass through the array performs operations in +** the followingratios: +** 4 adds, 4 subtracts, 5 multiplies, 3 divides +** (adds and subtracts being nearly the same operation) +*/ +while(loops--) +{ + for(i=0;i<arraysize;i++) + switch(jtable[i % 16]) + { + case 0: /* Add */ + AddSubInternalFPF(0,abase+i, + bbase+i, + cbase+i); + break; + case 1: /* Subtract */ + AddSubInternalFPF(1,abase+i, + bbase+i, + cbase+i); + break; + case 2: /* Multiply */ + MultiplyInternalFPF(abase+i, + bbase+i, + cbase+i); + break; + case 3: /* Divide */ + DivideInternalFPF(abase+i, + bbase+i, + cbase+i); + break; + } +#ifdef DEBUG +{ + ulong j[8]; /* we test 8 entries */ + int k; + ulong i; + char buffer[1024]; + if (number_of_loops==loops) /* the first loop */ + { + j[0]=(ulong)2; + j[1]=(ulong)6; + j[2]=(ulong)10; + j[3]=(ulong)14; + j[4]=(ulong)(arraysize-14); + j[5]=(ulong)(arraysize-10); + j[6]=(ulong)(arraysize-6); + j[7]=(ulong)(arraysize-2); + for(k=0;k<8;k++){ + i=j[k]; + InternalFPFToString(buffer,abase+i); + printf("%6ld: (%s) ",i,buffer); + switch(jtable[i % 16]) + { + case 0: strcpy(buffer,"+"); break; + case 1: strcpy(buffer,"-"); break; + case 2: strcpy(buffer,"*"); break; + case 3: strcpy(buffer,"/"); break; + } + printf("%s ",buffer); + InternalFPFToString(buffer,bbase+i); + printf("(%s) = ",buffer); + InternalFPFToString(buffer,cbase+i); + printf("%s\n",buffer); + } + } +} +#endif +} +return(StopStopwatch(elapsed)); +} + +/*********************** +** SetInternalFPFZero ** +************************ +** Set an internal floating-point-format number to zero. +** sign determines the sign of the zero. +*/ +static void SetInternalFPFZero(InternalFPF *dest, + uchar sign) +{ +int i; /* Index */ + +dest->type=IFPF_IS_ZERO; +dest->sign=sign; +dest->exp=MIN_EXP; +for(i=0;i<INTERNAL_FPF_PRECISION;i++) + dest->mantissa[i]=0; +return; +} + +/*************************** +** SetInternalFPFInfinity ** +**************************** +** Set an internal floating-point-format number to infinity. +** This can happen if the exponent exceeds MAX_EXP. +** As above, sign picks the sign of infinity. +*/ +static void SetInternalFPFInfinity(InternalFPF *dest, + uchar sign) +{ +int i; /* Index */ + +dest->type=IFPF_IS_INFINITY; +dest->sign=sign; +dest->exp=MIN_EXP; +for(i=0;i<INTERNAL_FPF_PRECISION;i++) + dest->mantissa[i]=0; +return; +} + +/********************** +** SetInternalFPFNaN ** +*********************** +** Set an internal floating-point-format number to Nan +** (not a number). Note that we "emulate" an 80x87 as far +** as the mantissa bits go. +*/ +static void SetInternalFPFNaN(InternalFPF *dest) +{ +int i; /* Index */ + +dest->type=IFPF_IS_NAN; +dest->exp=MAX_EXP; +dest->sign=1; +dest->mantissa[0]=0x4000; +for(i=1;i<INTERNAL_FPF_PRECISION;i++) + dest->mantissa[i]=0; + +return; +} + +/******************* +** IsMantissaZero ** +******************** +** Pass this routine a pointer to an internal floating point format +** number's mantissa. It checks for an all-zero mantissa. +** Returns 0 if it is NOT all zeros, !=0 otherwise. +*/ +static int IsMantissaZero(u16 *mant) +{ +int i; /* Index */ +int n; /* Return value */ + +n=0; +for(i=0;i<INTERNAL_FPF_PRECISION;i++) + n|=mant[i]; + +return(!n); +} + +/************** +** Add16Bits ** +*************** +** Add b, c, and carry. Retult in a. New carry in carry. +*/ +static void Add16Bits(u16 *carry, + u16 *a, + u16 b, + u16 c) +{ +u32 accum; /* Accumulator */ + +/* +** Do the work in the 32-bit accumulator so we can return +** the carry. +*/ +accum=(u32)b; +accum+=(u32)c; +accum+=(u32)*carry; +*carry=(u16)((accum & 0x00010000) ? 1 : 0); /* New carry */ +*a=(u16)(accum & 0xFFFF); /* Result is lo 16 bits */ +return; +} + +/************** +** Sub16Bits ** +*************** +** Additive inverse of above. +*/ +static void Sub16Bits(u16 *borrow, + u16 *a, + u16 b, + u16 c) +{ +u32 accum; /* Accumulator */ + +accum=(u32)b; +accum-=(u32)c; +accum-=(u32)*borrow; +*borrow=(u32)((accum & 0x00010000) ? 1 : 0); /* New borrow */ +*a=(u16)(accum & 0xFFFF); +return; +} + +/******************* +** ShiftMantLeft1 ** +******************** +** Shift a vector of 16-bit numbers left 1 bit. Also provides +** a carry bit, which is shifted in at the beginning, and +** shifted out at the end. +*/ +static void ShiftMantLeft1(u16 *carry, + u16 *mantissa) +{ +int i; /* Index */ +int new_carry; +u16 accum; /* Temporary holding placed */ + +for(i=INTERNAL_FPF_PRECISION-1;i>=0;i--) +{ accum=mantissa[i]; + new_carry=accum & 0x8000; /* Get new carry */ + accum=accum<<1; /* Do the shift */ + if(*carry) + accum|=1; /* Insert previous carry */ + *carry=new_carry; + mantissa[i]=accum; /* Return shifted value */ +} +return; +} + +/******************** +** ShiftMantRight1 ** +********************* +** Shift a mantissa right by 1 bit. Provides carry, as +** above +*/ +static void ShiftMantRight1(u16 *carry, + u16 *mantissa) +{ +int i; /* Index */ +int new_carry; +u16 accum; + +for(i=0;i<INTERNAL_FPF_PRECISION;i++) +{ accum=mantissa[i]; + new_carry=accum & 1; /* Get new carry */ + accum=accum>>1; + if(*carry) + accum|=0x8000; + *carry=new_carry; + mantissa[i]=accum; +} +return; +} + + +/***************************** +** StickyShiftMantRight ** +****************************** +** This is a shift right of the mantissa with a "sticky bit". +** I.E., if a carry of 1 is shifted out of the least significant +** bit, the least significant bit is set to 1. +*/ +static void StickyShiftRightMant(InternalFPF *ptr, + int amount) +{ +int i; /* Index */ +u16 carry; /* Self-explanatory */ +u16 *mantissa; + +mantissa=ptr->mantissa; + +if(ptr->type!=IFPF_IS_ZERO) /* Don't bother shifting a zero */ +{ + /* + ** If the amount of shifting will shift everyting + ** out of existence, then just clear the whole mantissa + ** and set the lowmost bit to 1. + */ + if(amount>=INTERNAL_FPF_PRECISION * 16) + { + for(i=0;i<INTERNAL_FPF_PRECISION-1;i++) + mantissa[i]=0; + mantissa[INTERNAL_FPF_PRECISION-1]=1; + } + else + for(i=0;i<amount;i++) + { + carry=0; + ShiftMantRight1(&carry,mantissa); + if(carry) + mantissa[INTERNAL_FPF_PRECISION-1] |= 1; + } +} +return; +} + + +/************************************************** +** POST ARITHMETIC PROCESSING ** +** (NORMALIZE, ROUND, OVERFLOW, AND UNDERFLOW) ** +**************************************************/ + +/************** +** normalize ** +*************** +** Normalize an internal-representation number. Normalization +** discards empty most-significant bits. +*/ +static void normalize(InternalFPF *ptr) +{ +u16 carry; + +/* +** As long as there's a highmost 0 bit, shift the significand +** left 1 bit. Each time you do this, though, you've +** gotta decrement the exponent. +*/ +while ((ptr->mantissa[0] & 0x8000) == 0) +{ + carry = 0; + ShiftMantLeft1(&carry, ptr->mantissa); + ptr->exp--; +} +return; +} + +/**************** +** denormalize ** +***************** +** Denormalize an internal-representation number. This means +** shifting it right until its exponent is equivalent to +** minimum_exponent. (You have to do this often in order +** to perform additions and subtractions). +*/ +static void denormalize(InternalFPF *ptr, + int minimum_exponent) +{ +long exponent_difference; + +if (IsMantissaZero(ptr->mantissa)) +{ + printf("Error: zero significand in denormalize\n"); +} + +exponent_difference = ptr->exp-minimum_exponent; +if (exponent_difference < 0) +{ + /* + ** The number is subnormal + */ + exponent_difference = -exponent_difference; + if (exponent_difference >= (INTERNAL_FPF_PRECISION * 16)) + { + /* Underflow */ + SetInternalFPFZero(ptr, ptr->sign); + } + else + { + ptr->exp+=exponent_difference; + StickyShiftRightMant(ptr, exponent_difference); + } +} +return; +} + + +/********************* +** RoundInternalFPF ** +********************** +** Round an internal-representation number. +** The kind of rounding we do here is simplest...referred to as +** "chop". "Extraneous" rightmost bits are simply hacked off. +*/ +void RoundInternalFPF(InternalFPF *ptr) +{ +/* int i; */ + +if (ptr->type == IFPF_IS_NORMAL || + ptr->type == IFPF_IS_SUBNORMAL) +{ + denormalize(ptr, MIN_EXP); + if (ptr->type != IFPF_IS_ZERO) + { + + /* clear the extraneous bits */ + ptr->mantissa[3] &= 0xfff8; +/* for (i=4; i<INTERNAL_FPF_PRECISION; i++) + { + ptr->mantissa[i] = 0; + } +*/ + /* + ** Check for overflow + */ +/* Does not do anything as ptr->exp is a short and MAX_EXP=37268 + if (ptr->exp > MAX_EXP) + { + SetInternalFPFInfinity(ptr, ptr->sign); + } +*/ + } +} +return; +} + +/******************************************************* +** ARITHMETIC OPERATIONS ON INTERNAL REPRESENTATION ** +*******************************************************/ + +/*************** +** choose_nan ** +**************** +** Called by routines that are forced to perform math on +** a pair of NaN's. This routine "selects" which NaN is +** to be returned. +*/ +static void choose_nan(InternalFPF *x, + InternalFPF *y, + InternalFPF *z, + int intel_flag) +{ +int i; + +/* +** Compare the two mantissas, +** return the larger. Note that we will be emulating +** an 80387 in this operation. +*/ +for (i=0; i<INTERNAL_FPF_PRECISION; i++) +{ + if (x->mantissa[i] > y->mantissa[i]) + { + memmove((void *)x,(void *)z,sizeof(InternalFPF)); + return; + } + if (x->mantissa[i] < y->mantissa[i]) + { + memmove((void *)y,(void *)z,sizeof(InternalFPF)); + return; + } +} + +/* +** They are equal +*/ +if (!intel_flag) + /* if the operation is addition */ + memmove((void *)x,(void *)z,sizeof(InternalFPF)); +else + /* if the operation is multiplication */ + memmove((void *)y,(void *)z,sizeof(InternalFPF)); +return; +} + + +/********************** +** AddSubInternalFPF ** +*********************** +** Adding or subtracting internal-representation numbers. +** Internal-representation numbers pointed to by x and y are +** added/subtracted and the result returned in z. +*/ +static void AddSubInternalFPF(uchar operation, + InternalFPF *x, + InternalFPF *y, + InternalFPF *z) +{ +int exponent_difference; +u16 borrow; +u16 carry; +int i; +InternalFPF locx,locy; /* Needed since we alter them */ + +/* +** Following big switch statement handles the +** various combinations of operand types. +*/ +switch ((x->type * IFPF_TYPE_COUNT) + y->type) +{ +case ZERO_ZERO: + memmove((void *)x,(void *)z,sizeof(InternalFPF)); + if (x->sign ^ y->sign ^ operation) + { + z->sign = 0; /* positive */ + } + break; + +case NAN_ZERO: +case NAN_SUBNORMAL: +case NAN_NORMAL: +case NAN_INFINITY: +case SUBNORMAL_ZERO: +case NORMAL_ZERO: +case INFINITY_ZERO: +case INFINITY_SUBNORMAL: +case INFINITY_NORMAL: + memmove((void *)x,(void *)z,sizeof(InternalFPF)); + break; + + +case ZERO_NAN: +case SUBNORMAL_NAN: +case NORMAL_NAN: +case INFINITY_NAN: + memmove((void *)y,(void *)z,sizeof(InternalFPF)); + break; + +case ZERO_SUBNORMAL: +case ZERO_NORMAL: +case ZERO_INFINITY: +case SUBNORMAL_INFINITY: +case NORMAL_INFINITY: + memmove((void *)y,(void *)z,sizeof(InternalFPF)); + z->sign ^= operation; + break; + +case SUBNORMAL_SUBNORMAL: +case SUBNORMAL_NORMAL: +case NORMAL_SUBNORMAL: +case NORMAL_NORMAL: + /* + ** Copy x and y to locals, since we may have + ** to alter them. + */ + memmove((void *)&locx,(void *)x,sizeof(InternalFPF)); + memmove((void *)&locy,(void *)y,sizeof(InternalFPF)); + + /* compute sum/difference */ + exponent_difference = locx.exp-locy.exp; + if (exponent_difference == 0) + { + /* + ** locx.exp == locy.exp + ** so, no shifting required + */ + if (locx.type == IFPF_IS_SUBNORMAL || + locy.type == IFPF_IS_SUBNORMAL) + z->type = IFPF_IS_SUBNORMAL; + else + z->type = IFPF_IS_NORMAL; + + /* + ** Assume that locx.mantissa > locy.mantissa + */ + z->sign = locx.sign; + z->exp= locx.exp; + } + else + if (exponent_difference > 0) + { + /* + ** locx.exp > locy.exp + */ + StickyShiftRightMant(&locy, + exponent_difference); + z->type = locx.type; + z->sign = locx.sign; + z->exp = locx.exp; + } + else /* if (exponent_difference < 0) */ + { + /* + ** locx.exp < locy.exp + */ + StickyShiftRightMant(&locx, + -exponent_difference); + z->type = locy.type; + z->sign = locy.sign ^ operation; + z->exp = locy.exp; + } + + if (locx.sign ^ locy.sign ^ operation) + { + /* + ** Signs are different, subtract mantissas + */ + borrow = 0; + for (i=(INTERNAL_FPF_PRECISION-1); i>=0; i--) + Sub16Bits(&borrow, + &z->mantissa[i], + locx.mantissa[i], + locy.mantissa[i]); + + if (borrow) + { + /* The y->mantissa was larger than the + ** x->mantissa leaving a negative + ** result. Change the result back to + ** an unsigned number and flip the + ** sign flag. + */ + z->sign = locy.sign ^ operation; + borrow = 0; + for (i=(INTERNAL_FPF_PRECISION-1); i>=0; i--) + { + Sub16Bits(&borrow, + &z->mantissa[i], + 0, + z->mantissa[i]); + } + } + else + { + /* The assumption made above + ** (i.e. x->mantissa >= y->mantissa) + ** was correct. Therefore, do nothing. + ** z->sign = x->sign; + */ + } + + if (IsMantissaZero(z->mantissa)) + { + z->type = IFPF_IS_ZERO; + z->sign = 0; /* positive */ + } + else + if (locx.type == IFPF_IS_NORMAL || + locy.type == IFPF_IS_NORMAL) + { + normalize(z); + } + } + else + { + /* signs are the same, add mantissas */ + carry = 0; + for (i=(INTERNAL_FPF_PRECISION-1); i>=0; i--) + { + Add16Bits(&carry, + &z->mantissa[i], + locx.mantissa[i], + locy.mantissa[i]); + } + + if (carry) + { + z->exp++; + carry=0; + ShiftMantRight1(&carry,z->mantissa); + z->mantissa[0] |= 0x8000; + z->type = IFPF_IS_NORMAL; + } + else + if (z->mantissa[0] & 0x8000) + z->type = IFPF_IS_NORMAL; + } + break; + +case INFINITY_INFINITY: + SetInternalFPFNaN(z); + break; + +case NAN_NAN: + choose_nan(x, y, z, 1); + break; +} + +/* +** All the math is done; time to round. +*/ +RoundInternalFPF(z); +return; +} + + +/************************ +** MultiplyInternalFPF ** +************************* +** Two internal-representation numbers x and y are multiplied; the +** result is returned in z. +*/ +static void MultiplyInternalFPF(InternalFPF *x, + InternalFPF *y, + InternalFPF *z) +{ +int i; +int j; +u16 carry; +u16 extra_bits[INTERNAL_FPF_PRECISION]; +InternalFPF locy; /* Needed since this will be altered */ +/* +** As in the preceding function, this large switch +** statement selects among the many combinations +** of operands. +*/ +switch ((x->type * IFPF_TYPE_COUNT) + y->type) +{ +case INFINITY_SUBNORMAL: +case INFINITY_NORMAL: +case INFINITY_INFINITY: +case ZERO_ZERO: +case ZERO_SUBNORMAL: +case ZERO_NORMAL: + memmove((void *)x,(void *)z,sizeof(InternalFPF)); + z->sign ^= y->sign; + break; + +case SUBNORMAL_INFINITY: +case NORMAL_INFINITY: +case SUBNORMAL_ZERO: +case NORMAL_ZERO: + memmove((void *)y,(void *)z,sizeof(InternalFPF)); + z->sign ^= x->sign; + break; + +case ZERO_INFINITY: +case INFINITY_ZERO: + SetInternalFPFNaN(z); + break; + +case NAN_ZERO: +case NAN_SUBNORMAL: +case NAN_NORMAL: +case NAN_INFINITY: + memmove((void *)x,(void *)z,sizeof(InternalFPF)); + break; + +case ZERO_NAN: +case SUBNORMAL_NAN: +case NORMAL_NAN: +case INFINITY_NAN: + memmove((void *)y,(void *)z,sizeof(InternalFPF)); + break; + + +case SUBNORMAL_SUBNORMAL: +case SUBNORMAL_NORMAL: +case NORMAL_SUBNORMAL: +case NORMAL_NORMAL: + /* + ** Make a local copy of the y number, since we will be + ** altering it in the process of multiplying. + */ + memmove((void *)&locy,(void *)y,sizeof(InternalFPF)); + + /* + ** Check for unnormal zero arguments + */ + if (IsMantissaZero(x->mantissa) || IsMantissaZero(y->mantissa)) + SetInternalFPFInfinity(z, 0); + + /* + ** Initialize the result + */ + if (x->type == IFPF_IS_SUBNORMAL || + y->type == IFPF_IS_SUBNORMAL) + z->type = IFPF_IS_SUBNORMAL; + else + z->type = IFPF_IS_NORMAL; + + z->sign = x->sign ^ y->sign; + z->exp = x->exp + y->exp ; + for (i=0; i<INTERNAL_FPF_PRECISION; i++) + { + z->mantissa[i] = 0; + extra_bits[i] = 0; + } + + for (i=0; i<(INTERNAL_FPF_PRECISION*16); i++) + { + /* + ** Get rightmost bit of the multiplier + */ + carry = 0; + ShiftMantRight1(&carry, locy.mantissa); + if (carry) + { + /* + ** Add the multiplicand to the product + */ + carry = 0; + for (j=(INTERNAL_FPF_PRECISION-1); j>=0; j--) + Add16Bits(&carry, + &z->mantissa[j], + z->mantissa[j], + x->mantissa[j]); + } + else + { + carry = 0; + } + + /* + ** Shift the product right. Overflow bits get + ** shifted into extra_bits. We'll use it later + ** to help with the "sticky" bit. + */ + ShiftMantRight1(&carry, z->mantissa); + ShiftMantRight1(&carry, extra_bits); + } + + /* + ** Normalize + ** Note that we use a "special" normalization routine + ** because we need to use the extra bits. (These are + ** bits that may have been shifted off the bottom that + ** we want to reclaim...if we can. + */ + while ((z->mantissa[0] & 0x8000) == 0) + { + carry = 0; + ShiftMantLeft1(&carry, extra_bits); + ShiftMantLeft1(&carry, z->mantissa); + z->exp--; + } + + /* + ** Set the sticky bit if any bits set in extra bits. + */ + if (IsMantissaZero(extra_bits)) + { + z->mantissa[INTERNAL_FPF_PRECISION-1] |= 1; + } + break; + +case NAN_NAN: + choose_nan(x, y, z, 0); + break; +} + +/* +** All math done...do rounding. +*/ +RoundInternalFPF(z); +return; +} + + +/********************** +** DivideInternalFPF ** +*********************** +** Divide internal FPF number x by y. Return result in z. +*/ +static void DivideInternalFPF(InternalFPF *x, + InternalFPF *y, + InternalFPF *z) +{ +int i; +int j; +u16 carry; +u16 extra_bits[INTERNAL_FPF_PRECISION]; +InternalFPF locx; /* Local for x number */ + +/* +** As with preceding function, the following switch +** statement selects among the various possible +** operands. +*/ +switch ((x->type * IFPF_TYPE_COUNT) + y->type) +{ +case ZERO_ZERO: +case INFINITY_INFINITY: + SetInternalFPFNaN(z); + break; + +case ZERO_SUBNORMAL: +case ZERO_NORMAL: + if (IsMantissaZero(y->mantissa)) + { + SetInternalFPFNaN(z); + break; + } + +case ZERO_INFINITY: +case SUBNORMAL_INFINITY: +case NORMAL_INFINITY: + SetInternalFPFZero(z, x->sign ^ y->sign); + break; + +case SUBNORMAL_ZERO: +case NORMAL_ZERO: + if (IsMantissaZero(x->mantissa)) + { + SetInternalFPFNaN(z); + break; + } + +case INFINITY_ZERO: +case INFINITY_SUBNORMAL: +case INFINITY_NORMAL: + SetInternalFPFInfinity(z, 0); + z->sign = x->sign ^ y->sign; + break; + +case NAN_ZERO: +case NAN_SUBNORMAL: +case NAN_NORMAL: +case NAN_INFINITY: + memmove((void *)x,(void *)z,sizeof(InternalFPF)); + break; + +case ZERO_NAN: +case SUBNORMAL_NAN: +case NORMAL_NAN: +case INFINITY_NAN: + memmove((void *)y,(void *)z,sizeof(InternalFPF)); + break; + +case SUBNORMAL_SUBNORMAL: +case NORMAL_SUBNORMAL: +case SUBNORMAL_NORMAL: +case NORMAL_NORMAL: + /* + ** Make local copy of x number, since we'll be + ** altering it in the process of dividing. + */ + memmove((void *)&locx,(void *)x,sizeof(InternalFPF)); + + /* + ** Check for unnormal zero arguments + */ + if (IsMantissaZero(locx.mantissa)) + { + if (IsMantissaZero(y->mantissa)) + SetInternalFPFNaN(z); + else + SetInternalFPFZero(z, 0); + break; + } + if (IsMantissaZero(y->mantissa)) + { + SetInternalFPFInfinity(z, 0); + break; + } + + /* + ** Initialize the result + */ + z->type = x->type; + z->sign = x->sign ^ y->sign; + z->exp = x->exp - y->exp + + ((INTERNAL_FPF_PRECISION * 16 * 2)); + for (i=0; i<INTERNAL_FPF_PRECISION; i++) + { + z->mantissa[i] = 0; + extra_bits[i] = 0; + } + + while ((z->mantissa[0] & 0x8000) == 0) + { + carry = 0; + ShiftMantLeft1(&carry, locx.mantissa); + ShiftMantLeft1(&carry, extra_bits); + + /* + ** Time to subtract yet? + */ + if (carry == 0) + for (j=0; j<INTERNAL_FPF_PRECISION; j++) + { + if (y->mantissa[j] > extra_bits[j]) + { + carry = 0; + goto no_subtract; + } + if (y->mantissa[j] < extra_bits[j]) + break; + } + /* + ** Divisor (y) <= dividend (x), subtract + */ + carry = 0; + for (j=(INTERNAL_FPF_PRECISION-1); j>=0; j--) + Sub16Bits(&carry, + &extra_bits[j], + extra_bits[j], + y->mantissa[j]); + carry = 1; /* 1 shifted into quotient */ + no_subtract: + ShiftMantLeft1(&carry, z->mantissa); + z->exp--; + } + break; + +case NAN_NAN: + choose_nan(x, y, z, 0); + break; +} + +/* +** Math complete...do rounding +*/ +RoundInternalFPF(z); +} + +/********************** +** LongToInternalFPF ** +** Int32ToInternalFPF ** +*********************** +** Convert a signed (long) 32-bit integer into an internal FPF number. +*/ +/* static void LongToInternalFPF(long mylong, */ +static void Int32ToInternalFPF(int32 mylong, + InternalFPF *dest) +{ +int i; /* Index */ +u16 myword; /* Used to hold converted stuff */ +/* +** Save the sign and get the absolute value. This will help us +** with 64-bit machines, since we use only the lower 32 +** bits just in case. (No longer necessary after we use int32.) +*/ +/* if(mylong<0L) */ +if(mylong<(int32)0) +{ dest->sign=1; + mylong=(int32)0-mylong; +} +else + dest->sign=0; +/* +** Prepare the destination floating point number +*/ +dest->type=IFPF_IS_NORMAL; +for(i=0;i<INTERNAL_FPF_PRECISION;i++) + dest->mantissa[i]=0; + +/* +** See if we've got a zero. If so, make the resultant FP +** number a true zero and go home. +*/ +if(mylong==0) +{ dest->type=IFPF_IS_ZERO; + dest->exp=0; + return; +} + +/* +** Not a true zero. Set the exponent to 32 (internal FPFs have +** no bias) and load the low and high words into their proper +** locations in the mantissa. Then normalize. The action of +** normalizing slides the mantissa bits into place and sets +** up the exponent properly. +*/ +dest->exp=32; +myword=(u16)((mylong >> 16) & 0xFFFFL); +dest->mantissa[0]=myword; +myword=(u16)(mylong & 0xFFFFL); +dest->mantissa[1]=myword; +normalize(dest); +return; +} + +#ifdef DEBUG +/************************ +** InternalFPFToString ** +************************* +** FOR DEBUG PURPOSES +** This routine converts an internal floating point representation +** number to a string. Used in debugging the package. +** Returns length of converted number. +** NOTE: dest must point to a buffer big enough to hold the +** result. Also, this routine does append a null (an effect +** of using the sprintf() function). It also returns +** a length count. +** NOTE: This routine returns 5 significant digits. Thats +** about all I feel safe with, given the method of +** conversion. It should be more than enough for programmers +** to determine whether the package is properly ported. +*/ +static int InternalFPFToString(char *dest, + InternalFPF *src) +{ +InternalFPF locFPFNum; /* Local for src (will be altered) */ +InternalFPF IFPF10; /* Floating-point 10 */ +InternalFPF IFPFComp; /* For doing comparisons */ +int msign; /* Holding for mantissa sign */ +int expcount; /* Exponent counter */ +int ccount; /* Character counter */ +int i,j,k; /* Index */ +u16 carryaccum; /* Carry accumulator */ +u16 mycarry; /* Local for carry */ + +/* +** Check first for the simple things...Nan, Infinity, Zero. +** If found, copy the proper string in and go home. +*/ +switch(src->type) +{ + case IFPF_IS_NAN: + memcpy(dest,"NaN",3); + return(3); + + case IFPF_IS_INFINITY: + if(src->sign==0) + memcpy(dest,"+Inf",4); + else + memcpy(dest,"-Inf",4); + return(4); + + case IFPF_IS_ZERO: + if(src->sign==0) + memcpy(dest,"+0",2); + else + memcpy(dest,"-0",2); + return(2); +} + +/* +** Move the internal number into our local holding area, since +** we'll be altering it to print it out. +*/ +memcpy((void *)&locFPFNum,(void *)src,sizeof(InternalFPF)); + +/* +** Set up a floating-point 10...which we'll use a lot in a minute. +*/ +/* LongToInternalFPF(10L,&IFPF10); */ +Int32ToInternalFPF((int32)10,&IFPF10); + +/* +** Save the mantissa sign and make it positive. +*/ +msign=src->sign; + +/* src->sign=0 */ /* bug, fixed Nov. 13, 1997 */ +(&locFPFNum)->sign=0; + +expcount=0; /* Init exponent counter */ + +/* +** See if the number is less than 10. If so, multiply +** the number repeatedly by 10 until it's not. For each +** multiplication, decrement a counter so we can keep track +** of the exponent. +*/ + +while(1) +{ AddSubInternalFPF(1,&locFPFNum,&IFPF10,&IFPFComp); + if(IFPFComp.sign==0) break; + MultiplyInternalFPF(&locFPFNum,&IFPF10,&IFPFComp); + expcount--; + memcpy((void *)&locFPFNum,(void *)&IFPFComp,sizeof(InternalFPF)); +} +/* +** Do the reverse of the above. As long as the number is +** greater than or equal to 10, divide it by 10. Increment the +** exponent counter for each multiplication. +*/ + +while(1) +{ + AddSubInternalFPF(1,&locFPFNum,&IFPF10,&IFPFComp); + if(IFPFComp.sign!=0) break; + DivideInternalFPF(&locFPFNum,&IFPF10,&IFPFComp); + expcount++; + memcpy((void *)&locFPFNum,(void *)&IFPFComp,sizeof(InternalFPF)); +} + +/* +** About time to start storing things. First, store the +** mantissa sign. +*/ +ccount=1; /* Init character counter */ +if(msign==0) + *dest++='+'; +else + *dest++='-'; + +/* +** At this point we know that the number is in the range +** 10 > n >=1. We need to "strip digits" out of the +** mantissa. We do this by treating the mantissa as +** an integer and multiplying by 10. (Not a floating-point +** 10, but an integer 10. Since this is debug code and we +** could care less about speed, we'll do it the stupid +** way and simply add the number to itself 10 times. +** Anything that makes it to the left of the implied binary point +** gets stripped off and emitted. We'll do this for +** 5 significant digits (which should be enough to +** verify things). +*/ +/* +** Re-position radix point +*/ +carryaccum=0; +while(locFPFNum.exp>0) +{ + mycarry=0; + ShiftMantLeft1(&mycarry,locFPFNum.mantissa); + carryaccum=(carryaccum<<1); + if(mycarry) carryaccum++; + locFPFNum.exp--; +} + +while(locFPFNum.exp<0) +{ + mycarry=0; + ShiftMantRight1(&mycarry,locFPFNum.mantissa); + locFPFNum.exp++; +} + +for(i=0;i<6;i++) + if(i==1) + { /* Emit decimal point */ + *dest++='.'; + ccount++; + } + else + { /* Emit a digit */ + *dest++=('0'+carryaccum); + ccount++; + + carryaccum=0; + memcpy((void *)&IFPF10, + (void *)&locFPFNum, + sizeof(InternalFPF)); + + /* Do multiply via repeated adds */ + for(j=0;j<9;j++) + { + mycarry=0; + for(k=(INTERNAL_FPF_PRECISION-1);k>=0;k--) + Add16Bits(&mycarry,&(IFPFComp.mantissa[k]), + locFPFNum.mantissa[k], + IFPF10.mantissa[k]); + carryaccum+=mycarry ? 1 : 0; + memcpy((void *)&locFPFNum, + (void *)&IFPFComp, + sizeof(InternalFPF)); + } + } + +/* +** Now move the 'E', the exponent sign, and the exponent +** into the string. +*/ +*dest++='E'; + +/* sprint is supposed to return an integer, but it caused problems on SunOS + * with the native cc. Hence we force it. + * Uwe F. Mayer + */ +ccount+=(int)sprintf(dest,"%4d",expcount); + +/* +** All done, go home. +*/ +return(ccount); + +} + +#endif |