emfloat.c
来自「开放源码的编译器open watcom 1.6.0版的源代码」· C语言 代码 · 共 1,275 行 · 第 1/3 页
C
1,275 行
#include <stdio.h>
#include "nmglobal.h"
#include "emfloat.h"
#include "timer.h"
/*
** emfloat.c
** BYTEmark (tm)
** BYTE's Native Mode Benchmarks
** Rick Grehan, BYTE Magazine.
*/
#ifndef MAC
#include <mem.h>
#endif
/*
** 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;
for(i=0;i<arraysize;i++)
{ LongToInternalFPF(randwc(50000L),&locFPF1);
LongToInternalFPF(randwc(50000L)+1L,&locFPF2);
DivideInternalFPF(&locFPF1,&locFPF2,abase+i);
LongToInternalFPF(randwc(50000L)+1L,&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, double *wat_time)
{
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;
/*
** Begin timing
*/
elapsed=StartStopwatch();
TimerOn();
/*
** 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;
}
}
TimerOff();
elapsed = StopStopwatch(elapsed);
if( wat_time ) {
*wat_time = TimerElapsed();
}
return(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)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?