home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-385-Vol-1of3.iso
/
p
/
ptv3n5.zip
/
FPNUM.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1992-09-20
|
7KB
|
234 lines
/*==================================================================
fpmath.cpp -- Fixed point math class [Listing #2]
by Robert N. Goldrich
Tested with Borland, Microsoft, Zortech, and TopSpeed C++
==================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <iostream.h>
#include "fpnum.h"
/*------------------------------------------------------------------
fixed point multiply
------------------------------------------------------------------*/
fpnum operator*( const fpnum& a, const fpnum& b )
{
long a_int = a.xx >> FP_FRCBITS ;
long b_int = b.xx >> FP_FRCBITS ;
long a_frac = a.xx - ( a_int << FP_FRCBITS ) ;
long b_frac = b.xx - ( b_int << FP_FRCBITS ) ;
long term1 = a_int * b_int ;
long term2 = ( b_frac >> 1 ) * ( a_frac >> 1 ) ;
// use >> 1 to avoid overflow
long term3 = ( a_int * b_frac + b_int * a_frac ) ;
long ans = (term1<<FP_FRCBITS)+ (term2>>(FP_FRCBITS-2))+ term3;
return fpnum( ans ) ; // private constructor
}
/*------------------------------------------------------------------
fixed point divide
------------------------------------------------------------------*/
fpnum operator/( const fpnum& a, const fpnum& b )
{
long whole = (a.xx / b.xx) << FP_FRCBITS ;
long fract = a.xx % b.xx ;
long abs2 = fract < 0 ? -fract : fract ; // | remainder |
long mask = 1L << (FP_TOTBITS-2) ;
// look for 1st non-zero bit, but not beyond FP_FRCBITS
int shift = 0 ;
while( !(mask & abs2 ) && shift < FP_FRCBITS ) {
shift++ ;
mask >>= 1 ;
}
fract = ( fract << shift ) / ( b.xx >> (FP_FRCBITS-shift) ) ;
return fpnum( whole + fract ) ; // private constructor
}
/*------------------------------------------------------------------
Round to nearest integer -- return an integer.
------------------------------------------------------------------*/
int round_to_int( const fpnum& a )
{
if( a.xx < 0 ) {
return -( (-a.xx+FP_HALF) >> FP_FRCBITS ) ;
}
else {
return ( a.xx+FP_HALF ) >> FP_FRCBITS ;
}
}
/*------------------------------------------------------------------
Truncates fraction and returns an integer.
------------------------------------------------------------------*/
int trunc_to_int( const fpnum& a )
{
if( a.xx < 0 ) {
return -( (-a.xx) >> FP_FRCBITS ) ;
}
else {
return a.xx >> FP_FRCBITS ;
}
}
/*------------------------------------------------------------------
fpnum to string / string to fp
------------------------------------------------------------------*/
char *fptoa( const fpnum& n, char *s )
{
/*--
Separate the integer and fractional parts. This part is complicated
because it must handle FP_MIN and FP_MAX. Work with a positive
fraction to make the bit shifting stuff work properly.
--*/
long whole = n.xx >> FP_FRCBITS ;
long frac = n.xx & ~( FP_ALLBITS << FP_FRCBITS ) ;
if( n.xx < 0 && frac != 0 ) {
frac = -frac ;
whole++ ;
}
/*--
Set-up for calculating the fractional part in base 10.
bit_value = 0.5 * 10 ^ FP_MAXDECPL
Do this only the first time the function is called.
--*/
static long first_bit_value ; // initialized to 0 at startup
if( !first_bit_value ) {
first_bit_value = 5 ;
for( int ii=0; ii<FP_MAXDECPL-1; ii++ ) {
first_bit_value *= 10 ;
}
}
//-- Mask off bits one by one, and add in their contribution if
// bit is set
long bit_value = first_bit_value ;
long mask = 1L << ( FP_FRCBITS - 1 ) ;
long decimal_frac = 0 ;
while( bit_value && mask ) {
if( mask & frac ) {
decimal_frac += bit_value ;
}
mask >>= 1 ;
bit_value >>= 1 ;
}
//-- Now have whole part and fract part in base 10. Compose string:
sprintf( s, "%ld.%0*ld", whole, FP_MAXDECPL, decimal_frac ) ;
//-- can truncate string here
return s ;
}
/*------------------------------------------------------------------
Convert string to fixed point number
------------------------------------------------------------------*/
static long ten[] = { 0, 10L, 100L, 1000L, 10000L, 100000L,
1000000L, 10000000L, 100000000L, 1000000000L } ;
#define NN ( ( FP_FRCBITS*301 ) / 1000 ) // 301 = log(2)
// maximum number of decimal digits to consider without overflow
fpnum atofp( char *str )
{
char buf[ 20 ] ;
int ii = 0 ;
int negative_flag = 0 ;
int hit_decpt = 0 ;
int max_digits = 2 * NN ;
fpnum val ; // initializes return value to zero
if( !str ) return val ;
while( isspace( *str ) ) str++ ; // get past the white space
if( !isdigit( *str ) ) { // check for + - . symbols
if( *str == '-' ) {
negative_flag = 1 ;
}
else if( *str == '+' ) {
}
else if( *str == '.' ) {
hit_decpt = 1 ;
}
else { // not a +, -, or .
return val ;
}
str++ ;
}
if( hit_decpt ) {
goto DEC_PT ; // it works for me
}
//-- Convert integer portion
while( isdigit(*str) ) {
buf[ ii++ ] = *str++ ;
}
buf[ ii ] = '\0' ;
val.xx = (long)( atoi( buf ) ) << FP_FRCBITS ;
if( *str++ != '.' ) { // ++ gets past decimal point
goto RETURN ;
}
//-- Convert decimal portion
DEC_PT: // now str points to 1st digit after decimal point
if( !isdigit( *str ) ) {
goto RETURN ;
}
ii=0 ;
while( isdigit(*str) && ii < NN ) {
buf[ ii++ ] = *str++ ;
}
buf[ ii ] = '\0' ;
val.xx += (2 * atol(buf) * FP_ONE + ten[ii] ) / ten[ii] / 2 ;
while( isdigit(*str) && ii < max_digits ) {
buf[ ii++ ] = *str++ ;
}
buf[ ii ] = '\0' ;
val.xx += (2 * atol(buf+NN) * FP_ONE + ten[ii] ) / ten[ii] / 2 ;
RETURN:
if( negative_flag ) val.xx = -val.xx ;
return val ;
}
#undef NN
/*------------------------------------------------------------------
stream methods
------------------------------------------------------------------*/
ostream& operator<<( ostream& o, const fpnum& n )
{
static char str[25] ;
fptoa( n, str ) ;
o << str ;
return o ;
}
/*------------------------------------------------------------------
initialize static class constants
------------------------------------------------------------------*/
#if 0
// Borland, Zortech, and TopSpeed do not allow these constants to be
// initialized with the private constructor, though they should.
const fpnum::fp_max( FP_MAX ) ; // maximum fpnum
const fpnum::fp_min( FP_MIN ) ; // minimum fpnum
const fpnum::fp_res( 1L ) ; // fpnum resolution
#endif