| /* |
| ** 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 "../pub/libvex_basictypes.h" |
| |
| static HWord (*serviceFn)(HWord,HWord) = 0; |
| |
| |
| ///////////////////////////////////////////////////////////////////// |
| ///////////////////////////////////////////////////////////////////// |
| |
| static char* my_strcpy ( char* dest, const char* src ) |
| { |
| char* dest_orig = dest; |
| while (*src) *dest++ = *src++; |
| *dest = 0; |
| return dest_orig; |
| } |
| |
| static void* my_memcpy ( void *dest, const void *src, int sz ) |
| { |
| const char *s = (const char *)src; |
| char *d = (char *)dest; |
| |
| while (sz--) |
| *d++ = *s++; |
| |
| return dest; |
| } |
| |
| static void* my_memmove( void *dst, const void *src, unsigned int len ) |
| { |
| register char *d; |
| register char *s; |
| if ( dst > src ) { |
| d = (char *)dst + len - 1; |
| s = (char *)src + len - 1; |
| while ( len >= 4 ) { |
| *d-- = *s--; |
| *d-- = *s--; |
| *d-- = *s--; |
| *d-- = *s--; |
| len -= 4; |
| } |
| while ( len-- ) { |
| *d-- = *s--; |
| } |
| } else if ( dst < src ) { |
| d = (char *)dst; |
| s = (char *)src; |
| while ( len >= 4 ) { |
| *d++ = *s++; |
| *d++ = *s++; |
| *d++ = *s++; |
| *d++ = *s++; |
| len -= 4; |
| } |
| while ( len-- ) { |
| *d++ = *s++; |
| } |
| } |
| return dst; |
| } |
| |
| ///////////////////////////////////////////////////////////////////// |
| |
| static void vexxx_log_bytes ( char* p, int n ) |
| { |
| int i; |
| for (i = 0; i < n; i++) |
| (*serviceFn)( 1, (int)p[i] ); |
| } |
| |
| /*---------------------------------------------------------*/ |
| /*--- vexxx_printf ---*/ |
| /*---------------------------------------------------------*/ |
| |
| /* This should be the only <...> include in the entire VEXXX library. |
| New code for vexxx_util.c should go above this point. */ |
| #include <stdarg.h> |
| |
| static HChar vexxx_toupper ( HChar c ) |
| { |
| if (c >= 'a' && c <= 'z') |
| return toHChar(c + ('A' - 'a')); |
| else |
| return c; |
| } |
| |
| static Int vexxx_strlen ( const HChar* str ) |
| { |
| Int i = 0; |
| while (str[i] != 0) i++; |
| return i; |
| } |
| |
| Bool vexxx_streq ( const HChar* s1, const HChar* s2 ) |
| { |
| while (True) { |
| if (*s1 == 0 && *s2 == 0) |
| return True; |
| if (*s1 != *s2) |
| return False; |
| s1++; |
| s2++; |
| } |
| } |
| |
| /* Some flags. */ |
| #define VG_MSG_SIGNED 1 /* The value is signed. */ |
| #define VG_MSG_ZJUSTIFY 2 /* Must justify with '0'. */ |
| #define VG_MSG_LJUSTIFY 4 /* Must justify on the left. */ |
| #define VG_MSG_PAREN 8 /* Parenthesize if present (for %y) */ |
| #define VG_MSG_COMMA 16 /* Add commas to numbers (for %d, %u) */ |
| |
| /* Copy a string into the buffer. */ |
| static UInt |
| myvprintf_str ( void(*send)(HChar), Int flags, Int width, HChar* str, |
| Bool capitalise ) |
| { |
| # define MAYBE_TOUPPER(ch) toHChar(capitalise ? vexxx_toupper(ch) : (ch)) |
| UInt ret = 0; |
| Int i, extra; |
| Int len = vexxx_strlen(str); |
| |
| if (width == 0) { |
| ret += len; |
| for (i = 0; i < len; i++) |
| send(MAYBE_TOUPPER(str[i])); |
| return ret; |
| } |
| |
| if (len > width) { |
| ret += width; |
| for (i = 0; i < width; i++) |
| send(MAYBE_TOUPPER(str[i])); |
| return ret; |
| } |
| |
| extra = width - len; |
| if (flags & VG_MSG_LJUSTIFY) { |
| ret += extra; |
| for (i = 0; i < extra; i++) |
| send(' '); |
| } |
| ret += len; |
| for (i = 0; i < len; i++) |
| send(MAYBE_TOUPPER(str[i])); |
| if (!(flags & VG_MSG_LJUSTIFY)) { |
| ret += extra; |
| for (i = 0; i < extra; i++) |
| send(' '); |
| } |
| |
| # undef MAYBE_TOUPPER |
| |
| return ret; |
| } |
| |
| /* Write P into the buffer according to these args: |
| * If SIGN is true, p is a signed. |
| * BASE is the base. |
| * If WITH_ZERO is true, '0' must be added. |
| * WIDTH is the width of the field. |
| */ |
| static UInt |
| myvprintf_int64 ( void(*send)(HChar), Int flags, Int base, Int width, ULong pL) |
| { |
| HChar buf[40]; |
| Int ind = 0; |
| Int i, nc = 0; |
| Bool neg = False; |
| HChar *digits = "0123456789ABCDEF"; |
| UInt ret = 0; |
| UInt p = (UInt)pL; |
| |
| if (base < 2 || base > 16) |
| return ret; |
| |
| if ((flags & VG_MSG_SIGNED) && (Int)p < 0) { |
| p = - (Int)p; |
| neg = True; |
| } |
| |
| if (p == 0) |
| buf[ind++] = '0'; |
| else { |
| while (p > 0) { |
| if ((flags & VG_MSG_COMMA) && 10 == base && |
| 0 == (ind-nc) % 3 && 0 != ind) |
| { |
| buf[ind++] = ','; |
| nc++; |
| } |
| buf[ind++] = digits[p % base]; |
| p /= base; |
| } |
| } |
| |
| if (neg) |
| buf[ind++] = '-'; |
| |
| if (width > 0 && !(flags & VG_MSG_LJUSTIFY)) { |
| for(; ind < width; ind++) { |
| //vassert(ind < 39); |
| buf[ind] = toHChar((flags & VG_MSG_ZJUSTIFY) ? '0': ' '); |
| } |
| } |
| |
| /* Reverse copy to buffer. */ |
| ret += ind; |
| for (i = ind -1; i >= 0; i--) { |
| send(buf[i]); |
| } |
| if (width > 0 && (flags & VG_MSG_LJUSTIFY)) { |
| for(; ind < width; ind++) { |
| ret++; |
| send(' '); // Never pad with zeroes on RHS -- changes the value! |
| } |
| } |
| return ret; |
| } |
| |
| |
| /* A simple vprintf(). */ |
| static |
| UInt vprintf_wrk ( void(*send)(HChar), const HChar *format, va_list vargs ) |
| { |
| UInt ret = 0; |
| int i; |
| int flags; |
| int width; |
| Bool is_long; |
| |
| /* We assume that vargs has already been initialised by the |
| caller, using va_start, and that the caller will similarly |
| clean up with va_end. |
| */ |
| |
| for (i = 0; format[i] != 0; i++) { |
| if (format[i] != '%') { |
| send(format[i]); |
| ret++; |
| continue; |
| } |
| i++; |
| /* A '%' has been found. Ignore a trailing %. */ |
| if (format[i] == 0) |
| break; |
| if (format[i] == '%') { |
| /* `%%' is replaced by `%'. */ |
| send('%'); |
| ret++; |
| continue; |
| } |
| flags = 0; |
| is_long = False; |
| width = 0; /* length of the field. */ |
| if (format[i] == '(') { |
| flags |= VG_MSG_PAREN; |
| i++; |
| } |
| /* If ',' follows '%', commas will be inserted. */ |
| if (format[i] == ',') { |
| flags |= VG_MSG_COMMA; |
| i++; |
| } |
| /* If '-' follows '%', justify on the left. */ |
| if (format[i] == '-') { |
| flags |= VG_MSG_LJUSTIFY; |
| i++; |
| } |
| /* If '0' follows '%', pads will be inserted. */ |
| if (format[i] == '0') { |
| flags |= VG_MSG_ZJUSTIFY; |
| i++; |
| } |
| /* Compute the field length. */ |
| while (format[i] >= '0' && format[i] <= '9') { |
| width *= 10; |
| width += format[i++] - '0'; |
| } |
| while (format[i] == 'l') { |
| i++; |
| is_long = True; |
| } |
| |
| switch (format[i]) { |
| case 'd': /* %d */ |
| flags |= VG_MSG_SIGNED; |
| if (is_long) |
| ret += myvprintf_int64(send, flags, 10, width, |
| (ULong)(va_arg (vargs, Long))); |
| else |
| ret += myvprintf_int64(send, flags, 10, width, |
| (ULong)(va_arg (vargs, Int))); |
| break; |
| case 'u': /* %u */ |
| if (is_long) |
| ret += myvprintf_int64(send, flags, 10, width, |
| (ULong)(va_arg (vargs, ULong))); |
| else |
| ret += myvprintf_int64(send, flags, 10, width, |
| (ULong)(va_arg (vargs, UInt))); |
| break; |
| case 'p': /* %p */ |
| ret += 2; |
| send('0'); |
| send('x'); |
| ret += myvprintf_int64(send, flags, 16, width, |
| (ULong)((HWord)va_arg (vargs, void *))); |
| break; |
| case 'x': /* %x */ |
| if (is_long) |
| ret += myvprintf_int64(send, flags, 16, width, |
| (ULong)(va_arg (vargs, ULong))); |
| else |
| ret += myvprintf_int64(send, flags, 16, width, |
| (ULong)(va_arg (vargs, UInt))); |
| break; |
| case 'c': /* %c */ |
| ret++; |
| send(toHChar(va_arg (vargs, int))); |
| break; |
| case 's': case 'S': { /* %s */ |
| char *str = va_arg (vargs, char *); |
| if (str == (char*) 0) str = "(null)"; |
| ret += myvprintf_str(send, flags, width, str, |
| toBool(format[i]=='S')); |
| break; |
| } |
| # if 0 |
| case 'y': { /* %y - print symbol */ |
| Char buf[100]; |
| Char *cp = buf; |
| Addr a = va_arg(vargs, Addr); |
| |
| if (flags & VG_MSG_PAREN) |
| *cp++ = '('; |
| if (VG_(get_fnname_w_offset)(a, cp, sizeof(buf)-4)) { |
| if (flags & VG_MSG_PAREN) { |
| cp += VG_(strlen)(cp); |
| *cp++ = ')'; |
| *cp = '\0'; |
| } |
| ret += myvprintf_str(send, flags, width, buf, 0); |
| } |
| break; |
| } |
| # endif |
| default: |
| break; |
| } |
| } |
| return ret; |
| } |
| |
| |
| /* A general replacement for printf(). Note that only low-level |
| debugging info should be sent via here. The official route is to |
| to use vg_message(). This interface is deprecated. |
| */ |
| static HChar myprintf_buf[1000]; |
| static Int n_myprintf_buf; |
| |
| static void add_to_myprintf_buf ( HChar c ) |
| { |
| if (c == '\n' || n_myprintf_buf >= 1000-10 /*paranoia*/ ) { |
| (*vexxx_log_bytes)( myprintf_buf, vexxx_strlen(myprintf_buf) ); |
| n_myprintf_buf = 0; |
| myprintf_buf[n_myprintf_buf] = 0; |
| } |
| myprintf_buf[n_myprintf_buf++] = c; |
| myprintf_buf[n_myprintf_buf] = 0; |
| } |
| |
| static UInt vexxx_printf ( const char *format, ... ) |
| { |
| UInt ret; |
| va_list vargs; |
| va_start(vargs,format); |
| |
| n_myprintf_buf = 0; |
| myprintf_buf[n_myprintf_buf] = 0; |
| ret = vprintf_wrk ( add_to_myprintf_buf, format, vargs ); |
| |
| if (n_myprintf_buf > 0) { |
| (*vexxx_log_bytes)( myprintf_buf, n_myprintf_buf ); |
| } |
| |
| va_end(vargs); |
| |
| return ret; |
| } |
| |
| /*---------------------------------------------------------------*/ |
| /*--- end vexxx_util.c ---*/ |
| /*---------------------------------------------------------------*/ |
| |
| |
| ///////////////////////////////////////////////////////////////////// |
| ///////////////////////////////////////////////////////////////////// |
| |
| //#include <stdio.h> |
| //#include <string.h> |
| //#include <malloc.h> |
| |
| typedef unsigned char uchar; |
| typedef unsigned int uint; |
| typedef unsigned short ushort; |
| typedef unsigned long ulong; |
| typedef int int32; /* Signed 32 bit integer */ |
| |
| #define INTERNAL_FPF_PRECISION 4 |
| #define CPUEMFLOATLOOPMAX 500000L |
| #define EMFARRAYSIZE 3000L |
| |
| typedef struct { |
| int adjust; /* Set adjust code */ |
| ulong request_secs; /* # of seconds requested */ |
| ulong arraysize; /* Size of array */ |
| ulong loops; /* Loops per iterations */ |
| double emflops; /* Results */ |
| } EmFloatStruct; |
| |
| |
| |
| /* Is this a 64 bit architecture? If so, this will define LONG64 */ |
| /* Uwe F. Mayer 15 November 1997 */ |
| // #include "pointer.h" |
| |
| #define u8 unsigned char |
| #define u16 unsigned short |
| #ifdef LONG64 |
| #define u32 unsigned int |
| #else |
| #define u32 unsigned long |
| #endif |
| #define uchar unsigned char |
| #define ulong unsigned long |
| |
| #define MAX_EXP 32767L |
| #define MIN_EXP (-32767L) |
| |
| #define IFPF_IS_ZERO 0 |
| #define IFPF_IS_SUBNORMAL 1 |
| #define IFPF_IS_NORMAL 2 |
| #define IFPF_IS_INFINITY 3 |
| #define IFPF_IS_NAN 4 |
| #define IFPF_TYPE_COUNT 5 |
| |
| #define ZERO_ZERO 0 |
| #define ZERO_SUBNORMAL 1 |
| #define ZERO_NORMAL 2 |
| #define ZERO_INFINITY 3 |
| #define ZERO_NAN 4 |
| |
| #define SUBNORMAL_ZERO 5 |
| #define SUBNORMAL_SUBNORMAL 6 |
| #define SUBNORMAL_NORMAL 7 |
| #define SUBNORMAL_INFINITY 8 |
| #define SUBNORMAL_NAN 9 |
| |
| #define NORMAL_ZERO 10 |
| #define NORMAL_SUBNORMAL 11 |
| #define NORMAL_NORMAL 12 |
| #define NORMAL_INFINITY 13 |
| #define NORMAL_NAN 14 |
| |
| #define INFINITY_ZERO 15 |
| #define INFINITY_SUBNORMAL 16 |
| #define INFINITY_NORMAL 17 |
| #define INFINITY_INFINITY 18 |
| #define INFINITY_NAN 19 |
| |
| #define NAN_ZERO 20 |
| #define NAN_SUBNORMAL 21 |
| #define NAN_NORMAL 22 |
| #define NAN_INFINITY 23 |
| #define NAN_NAN 24 |
| #define OPERAND_ZERO 0 |
| #define OPERAND_SUBNORMAL 1 |
| #define OPERAND_NORMAL 2 |
| #define OPERAND_INFINITY 3 |
| #define OPERAND_NAN 4 |
| |
| typedef struct |
| { |
| u8 type; /* Indicates, NORMAL, SUBNORMAL, etc. */ |
| u8 sign; /* Mantissa sign */ |
| short exp; /* Signed exponent...no bias */ |
| u16 mantissa[INTERNAL_FPF_PRECISION]; |
| } InternalFPF; |
| |
| static |
| void SetupCPUEmFloatArrays(InternalFPF *abase, |
| InternalFPF *bbase, InternalFPF *cbase, ulong arraysize); |
| static |
| ulong DoEmFloatIteration(InternalFPF *abase, |
| InternalFPF *bbase, InternalFPF *cbase, |
| ulong arraysize, ulong loops); |
| |
| static void SetInternalFPFZero(InternalFPF *dest, |
| uchar sign); |
| static void SetInternalFPFInfinity(InternalFPF *dest, |
| uchar sign); |
| static void SetInternalFPFNaN(InternalFPF *dest); |
| static int IsMantissaZero(u16 *mant); |
| static void Add16Bits(u16 *carry,u16 *a,u16 b,u16 c); |
| static void Sub16Bits(u16 *borrow,u16 *a,u16 b,u16 c); |
| static void ShiftMantLeft1(u16 *carry,u16 *mantissa); |
| static void ShiftMantRight1(u16 *carry,u16 *mantissa); |
| static void StickyShiftRightMant(InternalFPF *ptr,int amount); |
| static void normalize(InternalFPF *ptr); |
| static void denormalize(InternalFPF *ptr,int minimum_exponent); |
| static void RoundInternalFPF(InternalFPF *ptr); |
| static void choose_nan(InternalFPF *x,InternalFPF *y,InternalFPF *z, |
| int intel_flag); |
| static void AddSubInternalFPF(uchar operation,InternalFPF *x, |
| InternalFPF *y,InternalFPF *z); |
| static void MultiplyInternalFPF(InternalFPF *x,InternalFPF *y, |
| InternalFPF *z); |
| static void DivideInternalFPF(InternalFPF *x,InternalFPF *y, |
| InternalFPF *z); |
| |
| static void Int32ToInternalFPF(int32 mylong, |
| InternalFPF *dest); |
| static int InternalFPFToString(char *dest, |
| InternalFPF *src); |
| |
| static int32 randnum(int32 lngval); |
| |
| static int32 randwc(int32 num) |
| { |
| return(randnum((int32)0)%num); |
| } |
| |
| static int32 randw[2] = { (int32)13 , (int32)117 }; |
| static int32 randnum(int32 lngval) |
| { |
| register int32 interm; |
| |
| if (lngval!=(int32)0) |
| { randw[0]=(int32)13; randw[1]=(int32)117; } |
| |
| interm=(randw[0]*(int32)254754+randw[1]*(int32)529562)%(int32)999563; |
| randw[1]=randw[0]; |
| randw[0]=interm; |
| return(interm); |
| } |
| |
| |
| static |
| void SetupCPUEmFloatArrays(InternalFPF *abase, |
| InternalFPF *bbase, |
| InternalFPF *cbase, |
| ulong arraysize) |
| { |
| ulong i; |
| InternalFPF locFPF1,locFPF2; |
| |
| 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; |
| } |
| |
| |
| static char* str1 = "loops %d\n"; |
| static |
| ulong DoEmFloatIteration(InternalFPF *abase, |
| InternalFPF *bbase, |
| InternalFPF *cbase, |
| ulong arraysize, ulong loops) |
| { |
| static uchar jtable[16] = {0,0,0,0,1,1,1,1,2,2,2,2,2,3,3,3}; |
| ulong i; |
| int number_of_loops; |
| loops = 100; |
| number_of_loops=loops-1; /* the index of the first loop we run */ |
| |
| vexxx_printf(str1, (int)loops); |
| |
| /* |
| ** 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) |
| */ |
| |
| { |
| 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; |
| } |
| { |
| ulong j[8]; /* we test 8 entries */ |
| int k; |
| ulong i; |
| char buffer[1024]; |
| if (100==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); |
| vexxx_printf("%6d: (%s) ",i,buffer); |
| switch(jtable[i % 16]) |
| { |
| case 0: my_strcpy(buffer,"+"); break; |
| case 1: my_strcpy(buffer,"-"); break; |
| case 2: my_strcpy(buffer,"*"); break; |
| case 3: my_strcpy(buffer,"/"); break; |
| } |
| vexxx_printf("%s ",buffer); |
| InternalFPFToString(buffer,bbase+i); |
| vexxx_printf("(%s) = ",buffer); |
| InternalFPFToString(buffer,cbase+i); |
| vexxx_printf("%s\n",buffer); |
| } |
| return 0; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| /*********************** |
| ** 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)) |
| { |
| vexxx_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]) |
| { |
| my_memmove((void *)x,(void *)z,sizeof(InternalFPF)); |
| return; |
| } |
| if (x->mantissa[i] < y->mantissa[i]) |
| { |
| my_memmove((void *)y,(void *)z,sizeof(InternalFPF)); |
| return; |
| } |
| } |
| |
| /* |
| ** They are equal |
| */ |
| if (!intel_flag) |
| /* if the operation is addition */ |
| my_memmove((void *)x,(void *)z,sizeof(InternalFPF)); |
| else |
| /* if the operation is multiplication */ |
| my_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: |
| my_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: |
| my_memmove((void *)x,(void *)z,sizeof(InternalFPF)); |
| break; |
| |
| |
| case ZERO_NAN: |
| case SUBNORMAL_NAN: |
| case NORMAL_NAN: |
| case INFINITY_NAN: |
| my_memmove((void *)y,(void *)z,sizeof(InternalFPF)); |
| break; |
| |
| case ZERO_SUBNORMAL: |
| case ZERO_NORMAL: |
| case ZERO_INFINITY: |
| case SUBNORMAL_INFINITY: |
| case NORMAL_INFINITY: |
| my_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. |
| */ |
| my_memmove((void *)&locx,(void *)x,sizeof(InternalFPF)); |
| my_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: |
| my_memmove((void *)x,(void *)z,sizeof(InternalFPF)); |
| z->sign ^= y->sign; |
| break; |
| |
| case SUBNORMAL_INFINITY: |
| case NORMAL_INFINITY: |
| case SUBNORMAL_ZERO: |
| case NORMAL_ZERO: |
| my_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: |
| my_memmove((void *)x,(void *)z,sizeof(InternalFPF)); |
| break; |
| |
| case ZERO_NAN: |
| case SUBNORMAL_NAN: |
| case NORMAL_NAN: |
| case INFINITY_NAN: |
| my_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. |
| */ |
| my_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: |
| my_memmove((void *)x,(void *)z,sizeof(InternalFPF)); |
| break; |
| |
| case ZERO_NAN: |
| case SUBNORMAL_NAN: |
| case NORMAL_NAN: |
| case INFINITY_NAN: |
| my_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. |
| */ |
| my_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; |
| } |
| |
| #if 1 |
| /************************ |
| ** 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: |
| my_memcpy(dest,"NaN",3); |
| return(3); |
| |
| case IFPF_IS_INFINITY: |
| if(src->sign==0) |
| my_memcpy(dest,"+Inf",4); |
| else |
| my_memcpy(dest,"-Inf",4); |
| return(4); |
| |
| case IFPF_IS_ZERO: |
| if(src->sign==0) |
| my_memcpy(dest,"+0",2); |
| else |
| my_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. |
| */ |
| my_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--; |
| my_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++; |
| my_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; |
| my_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; |
| my_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 |
| */ |
| if (expcount < 0) { |
| *dest++ = '-'; |
| expcount =- expcount; |
| } |
| else *dest++ = ' '; |
| |
| *dest++ = (char)(expcount + '0'); |
| *dest++ = 0; |
| |
| ccount += 3; |
| /* |
| ** All done, go home. |
| */ |
| return(ccount); |
| |
| } |
| |
| #endif |
| |
| |
| |
| //////////////////////////////////////////////////////////////////////// |
| static |
| void* AllocateMemory ( unsigned long n, int* p ) |
| { |
| *p = 0; |
| void* r = (void*) (*serviceFn)(2,n); |
| return r; |
| } |
| static |
| void FreeMemory ( void* p, int* zz ) |
| { |
| *zz = 0; |
| // free(p); |
| } |
| |
| |
| |
| /************** |
| ** DoEmFloat ** |
| *************** |
| ** Perform the floating-point emulation routines portion of the |
| ** CPU benchmark. Returns the operations per second. |
| */ |
| static |
| void DoEmFloat(void) |
| { |
| EmFloatStruct *locemfloatstruct; /* Local structure */ |
| InternalFPF *abase; /* Base of A array */ |
| InternalFPF *bbase; /* Base of B array */ |
| InternalFPF *cbase; /* Base of C array */ |
| ulong tickcount; /* # of ticks */ |
| char *errorcontext; /* Error context string pointer */ |
| int systemerror; /* For holding error code */ |
| ulong loops; /* # of loops */ |
| |
| /* |
| ** Link to global structure |
| */ |
| EmFloatStruct global_emfloatstruct; |
| global_emfloatstruct.adjust = 0; |
| global_emfloatstruct.request_secs = 0; |
| global_emfloatstruct.arraysize = 100; |
| global_emfloatstruct.loops = 1; |
| global_emfloatstruct.emflops = 0.0; |
| locemfloatstruct=&global_emfloatstruct; |
| |
| /* |
| ** Set the error context |
| */ |
| errorcontext="CPU:Floating Emulation"; |
| |
| |
| abase=(InternalFPF *)AllocateMemory(locemfloatstruct->arraysize*sizeof(InternalFPF), |
| &systemerror); |
| |
| bbase=(InternalFPF *)AllocateMemory(locemfloatstruct->arraysize*sizeof(InternalFPF), |
| &systemerror); |
| |
| cbase=(InternalFPF *)AllocateMemory(locemfloatstruct->arraysize*sizeof(InternalFPF), |
| &systemerror); |
| |
| /* |
| ** Set up the arrays |
| */ |
| SetupCPUEmFloatArrays(abase,bbase,cbase,locemfloatstruct->arraysize); |
| |
| loops=100; |
| tickcount=DoEmFloatIteration(abase,bbase,cbase, |
| locemfloatstruct->arraysize, |
| loops); |
| |
| FreeMemory((void *)abase,&systemerror); |
| FreeMemory((void *)bbase,&systemerror); |
| FreeMemory((void *)cbase,&systemerror); |
| |
| return; |
| } |
| |
| ////////////////// |
| void entry ( HWord(*f)(HWord,HWord) ) |
| { |
| serviceFn = f; |
| vexxx_printf("starting\n"); |
| DoEmFloat(); |
| (*serviceFn)(0,0); |
| } |