home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fresh Fish 7
/
FreshFishVol7.bin
/
bbs
/
gnu
/
libg++-2.6-fsf.lha
/
libg++-2.6
/
libg++
/
src
/
Fix.cc
< prev
next >
Wrap
C/C++ Source or Header
|
1994-06-28
|
13KB
|
664 lines
// This may look like C code, but it is really -*- C++ -*-
/*
Copyright (C) 1989 Free Software Foundation
written by Doug Lea (dl@rocky.oswego.edu)
This file is part of the GNU C++ Library. This library is free
software; you can redistribute it and/or modify it under the terms of
the GNU Library General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your
option) any later version. This library is distributed in the hope
that it will be useful, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
//
// Fix.cc : variable length fixed point data type class functions
//
#ifdef __GNUG__
#pragma implementation
#endif
#include <Fix.h>
#include <std.h>
#include <Obstack.h>
#include <AllocRing.h>
#include <strstream.h>
// member constants
const _G_uint16_t Fix::min_length;
const _G_uint16_t Fix::max_length;
const double Fix::min_value;
const double Fix::max_value;
// default parameters
_G_uint16_t Fix::default_length = 16;
int Fix::default_print_width = 8;
Fix::PEH Fix::overflow_handler = Fix::overflow_saturate;
Fix::Rep Fix::Rep_0 = { 16, 1, 1, { 0 } };
Fix::Rep Fix::Rep_m1 = { 16, 1, 1, { 0x8000 } };
Fix::Rep Fix::Rep_quotient_bump = { 16, 1, 1, { 0x4000 } };
// error handling
void
Fix::default_error_handler(const char* msg)
{
cerr << "Fix: " << msg << "\n";
abort();
}
void
Fix::default_range_error_handler(const char* msg)
{
cerr << "Fix: range error in " << msg << "\n";
//abort();
}
one_arg_error_handler_t
Fix::error_handler = Fix::default_error_handler,
Fix::range_error_handler = Fix::default_range_error_handler;
one_arg_error_handler_t
Fix::set_error_handler(one_arg_error_handler_t f)
{
one_arg_error_handler_t old = error_handler;
error_handler = f;
return old;
}
one_arg_error_handler_t
Fix::set_range_error_handler(one_arg_error_handler_t f)
{
one_arg_error_handler_t old = range_error_handler;
range_error_handler = f;
return old;
}
void
Fix::error(const char* msg)
{
error_handler(msg);
}
void
Fix::range_error(const char* msg)
{
range_error_handler(msg);
}
// Fix::Rep allocation and initialization functions
static inline Fix::Rep*
_new_Fix(_G_uint16_t len)
{
int siz = (((_G_uint32_t) len + 15) >> 4);
if (siz <= 0) siz = 1;
unsigned int allocsiz = (sizeof(Fix::Rep) + (siz - 1) * sizeof(_G_uint16_t));
Fix::Rep* z = (Fix::Rep*)(new char[allocsiz]);
memset(z, 0, allocsiz);
z->len = len;
z->siz = siz;
z->ref = 1;
return z;
}
Fix::Rep*
Fix::new_Fix(_G_uint16_t len)
{
return _new_Fix(len);
}
Fix::Rep*
Fix::new_Fix(_G_uint16_t len, const Rep* x)
{
Rep* z = _new_Fix(len);
return copy(x,z);
}
Fix::Rep*
Fix::new_Fix(_G_uint16_t len, double d)
{
Rep* z = _new_Fix(len);
if ( d == max_value )
{
z->s[0] = 0x7fff;
for ( int i=1; i < z->siz; i++ )
z->s[i] = 0xffff;
}
else if ( d < min_value || d > max_value )
range_error("declaration");
else
{
if (d < 0)
d += 2.0;
d *= 32768;
for ( int i=0; i < z->siz; i++ )
{
z->s[i] = (_G_uint16_t )d;
d -= z->s[i];
d *= 65536;
}
if ( d >= 32768 )
z->s[z->siz-1]++;
}
mask(z);
return z;
}
// convert to a double
double
value(const Fix& x)
{
double d = 0.0;
for ( int i=x.rep->siz-1; i >= 0; i-- )
{
d += x.rep->s[i];
d *= 1./65536.;
}
d *= 2.;
return d < 1. ? d : d - 2.;
}
// extract mantissa to Integer
Integer
mantissa(const Fix& x)
{
Integer a = 1, b=1;
for ( int i=0; i < x.rep->siz; i++ )
{
a <<= 16;
a += x.rep->s[i];
b <<= 16;
}
return a-b;
}
// comparison functions
inline static int
docmp(const _G_uint16_t* x, const _G_uint16_t* y, int siz)
{
int diff = (_G_int16_t )*x - (_G_int16_t )*y;
while ( --siz && !diff )
diff = (_G_int32_t )(_G_uint32_t )*++x - (_G_int32_t )(_G_uint32_t )*++y;
return diff;
}
inline static int
docmpz(const _G_uint16_t* x, int siz)
{
while ( siz-- )
if ( *x++ ) return 1;
return 0;
}
int
Fix::compare(const Rep* x, const Rep* y)
{
if ( x->siz == y->siz )
return docmp(x->s, y->s, x->siz);
else
{
int r;
const Rep* longer, *shorter;
if ( x->siz > y->siz )
{
longer = x;
shorter = y;
r = 1;
}
else
{
longer = y;
shorter = x;
r = -1;
}
int diff = docmp(x->s, y->s, shorter->siz);
if ( diff )
return diff;
else if ( docmpz(&longer->s[shorter->siz], longer->siz-shorter->siz) )
return r;
else
return 0;
}
}
// arithmetic functions
Fix::Rep*
Fix::add(const Rep* x, const Rep* y, Rep* r)
{
_G_uint16_t xsign = x->s[0], ysign = y->s[0];
const Rep* longer, *shorter;
if ( x->len >= y->len )
longer = x, shorter = y;
else
longer = y, shorter = x;
if ( r == NULL )
r = new_Fix(longer->len);
for ( int i=r->siz-1; i >= longer->siz; i-- )
r->s[i] = 0;
for ( ; i >= shorter->siz; i-- )
r->s[i] = longer->s[i];
_G_uint32_t sum = 0, carry = 0;
for ( ; i >= 0; i-- )
{
sum = carry + (_G_uint32_t )x->s[i] + (_G_uint32_t )y->s[i];
carry = sum >> 16;
r->s[i] = sum;
}
if ( (xsign ^ sum) & (ysign ^ sum) & 0x8000 )
overflow_handler(r);
return r;
}
Fix::Rep*
Fix::subtract(const Rep* x, const Rep* y, Rep* r)
{
_G_uint16_t xsign = x->s[0], ysign = y->s[0];
const Rep* longer, *shorter;
if ( x->len >= y->len )
longer = x, shorter = y;
else
longer = y, shorter = x;
if ( r == NULL )
r = new_Fix(longer->len);
for ( int i=r->siz-1; i >= longer->siz; i-- )
r->s[i] = 0;
for ( ; i >= shorter->siz; i-- )
r->s[i] = (longer == x ? x->s[i] : -y->s[i]);
_G_int16_t carry = 0;
_G_uint32_t sum = 0;
for ( ; i >= 0; i-- )
{
sum = (_G_int32_t )carry + (_G_uint32_t )x->s[i] - (_G_uint32_t )y->s[i];
carry = sum >> 16;
r->s[i] = sum;
}
if ( (xsign ^ sum) & (~ysign ^ sum) & 0x8000 )
overflow_handler(r);
return r;
}
Fix::Rep*
Fix::multiply(const Rep* x, const Rep* y, Rep* r)
{
if ( r == NULL )
r = new_Fix(x->len + y->len);
int xsign = x->s[0] & 0x8000,
ysign = y->s[0] & 0x8000;
Fix X(x->len), Y(y->len);
if ( xsign )
x = negate(x,X.rep);
if ( ysign )
y = negate(y,Y.rep);
for ( int i=0; i < r->siz; i++ )
r->s[i] = 0;
for ( i=x->siz-1; i >= 0; i-- )
{
_G_uint32_t carry = 0;
for ( int j=y->siz-1; j >= 0; j-- )
{
int k = i + j + 1;
_G_uint32_t a = (_G_uint32_t )x->s[i] * (_G_uint32_t )y->s[j];
_G_uint32_t b = ((a << 1) & 0xffff) + carry;
if ( k < r->siz )
{
b += r->s[k];
r->s[k] = b;
}
if ( k < (int)r->siz + 1 )
carry = (a >> 15) + (b >> 16);
}
r->s[i] = carry;
}
if ( xsign != ysign )
negate(r,r);
return r;
}
Fix::Rep*
Fix::multiply(const Rep* x, int y, Rep* r)
{
if ( y != (_G_int16_t )y )
range_error("multiply by int -- int too large");
if ( r == NULL )
r = new_Fix(x->len);
for ( int i=r->siz-1; i >= x->siz; i-- )
r->s[i] = 0;
_G_int32_t a, carry = 0;
for ( ; i > 0; i-- )
{
a = (_G_int32_t) (_G_uint32_t )x->s[i] * y + carry;
r->s[i] = a;
carry = a >> 16; // assumes arithmetic right shift
}
a = (_G_int32_t) (_G_int16_t )x->s[0] * y + carry;
r->s[0] = a;
a &= 0xffff8000L;
if ( a != 0xffff8000L && a != 0L ) {
r->s[0] = 0x8000 ^ x->s[0] ^ y;
overflow_handler(r);
}
return r;
}
Fix::Rep*
Fix::divide(const Rep* x, const Rep* y, Rep* q, Rep* r)
{
int xsign = x->s[0] & 0x8000,
ysign = y->s[0] & 0x8000;
if ( q == NULL )
q = new_Fix(x->len);
copy(&Rep_0,q);
if ( r == NULL )
r = new_Fix(x->len + y->len - 1);
if ( xsign )
negate(x,r);
else
copy(x,r);
Fix Y(y->len);
Rep* y2 = ( ysign ? negate(y,Y.rep) : copy(y,Y.rep) );
if ( !compare(y2) )
range_error("division -- division by zero");
else if ( compare(x,y2) >= 0 )