home *** CD-ROM | disk | FTP | other *** search
- // This may look like C code, but it is really -*- C++ -*-
- /*
- Copyright (C) 1988 Free Software Foundation
- written by Kurt Baudendistel (gt-eedsp!baud@gatech.edu)
- adapted for libg++ by Doug Lea (dl@rocky.oswego.edu)
-
- This file is part of GNU CC.
-
- GNU CC is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY. No author or distributor
- accepts responsibility to anyone for the consequences of using it
- or for whether it serves any particular purpose or works at all,
- unless he says so in writing. Refer to the GNU CC General Public
- License for full details.
-
- Everyone is granted permission to copy, modify and redistribute
- GNU CC, but only under the conditions described in the
- GNU CC General Public License. A copy of this license is
- supposed to have been given to you along with GNU CC so you
- can know your rights and responsibilities. It should be in a
- file named COPYING. Among other things, the copyright notice
- and this notice must be preserved on all copies.
- */
-
- //
- // Fix24.cc : fixed precision class support functions
- //
-
- #include <Fix24.h>
-
- const double
- Fix24_fs = 2147483648.,
- Fix24_mult = Fix24_fs,
- Fix24_div = 1./Fix24_fs,
- Fix24_max = 1. - .5/Fix24_fs,
- Fix24_min = -1.;
-
- const unsigned long
- Fix24_msb = 0x80000000L,
- Fix24_lsb = 0x00000100L,
- Fix24_m_max = 0x7fffff00L,
- Fix24_m_min = 0x80000000L;
-
- const double
- Fix48_fs = 4611686018427387904.,
- Fix48_max = 1. - .5/Fix48_fs,
- Fix48_min = -1.,
- Fix48_div_u = 1./Fix24_fs,
- Fix48_div_l = 1./Fix48_fs;
-
- const twolongs
- Fix48_msb = { 0x80000000L, 0L },
- Fix48_lsb = { 0L, 0x00000100L },
- Fix48_m_max = { 0x7fffff00L, 0xffffff00L },
- Fix48_m_min = { 0x80000000L, 0L };
-
- // basic operators too large to be inline
-
- long Fix24::assign(double d)
- {
- if (d == 1.0)
- return Fix24_m_max;
- else if (d > Fix24_max)
- {
- long i = Fix24_m_max;
- range_error(i);
- return i;
- }
- else if (d < Fix24_min)
- {
- long i = Fix24_m_min;
- range_error(i);
- return i;
- }
- else {
- d *= Fix24_mult;
- return (long)(d + ((d >= 0)? 0.5 : -0.5));
- }
- }
-
- twolongs Fix48::assign(double d)
- {
- if (d == 1.0)
- return Fix48_m_max;
- else if (d > Fix48_max)
- {
- twolongs i = Fix48_m_max;
- range_error(i);
- return i;
- }
- else if (d < Fix48_min)
- {
- twolongs i = Fix48_m_min;
- range_error(i);
- return i;
- }
- else {
- twolongs i;
- i.u = (long)(d *= Fix24_mult);
- i.l = (long)((d - (double)i.u) * Fix24_mult);
- return i;
- }
- }
-
-
- Fix48 operator * (Fix24& a, Fix24& b)
- {
- // break a and b into lo and hi parts, and do a multiple-precision
- // multiply, with rounding
-
- int apos = (a.m >= 0);
- unsigned long ua = (apos)? a.m : - a.m;
- ua <<= 1; // ua is biased so result will be 47 bit mantissa, not 46:
- unsigned long hi_a = (ua >> 16) & ((1 << 16) - 1);
- unsigned long lo_a = ua & ((1 << 16) - 1);
-
- int bpos = (b.m >= 0);
- unsigned long ub = (bpos)? b.m : -b.m;
- unsigned long hi_b = (ub >> 16) & ((1 << 16) - 1);
- unsigned long lo_b = ub & ((1 << 16) - 1);
-
- unsigned long
- hi_r = hi_a * hi_b,
- mi_r = hi_a * lo_b + lo_a * hi_b,
- lo_r = lo_a * lo_b,
- rl = ((hi_r << 16) & 0x00ffffffL) + (mi_r & 0x00ffffffL) + (lo_r >> 16);
- twolongs r = {
- (hi_r & 0xffffff00L) + ((mi_r >> 16) & 0x0000ff00L)
- + ((rl >> 16) & 0x0000ff00L),
- rl << 8
- };
-
- if ( apos != bpos ) {
- unsigned long l = r.l;
- r.l = -r.l;
- r.u = ~r.u + ((l ^ r.l) & Fix24_msb ? 0 : Fix24_lsb);
- }
- return r;
- }
-
- Fix24 operator / (Fix24& a, Fix24& b)
- {
- long q;
- int apos = (a.m >= 0);
- unsigned long la = (apos)? a.m : -a.m;
- int bpos = (b.m >= 0);
- unsigned long lb = (bpos)? b.m: -b.m;
- if (la >= lb)
- {
- q = (apos == bpos)? Fix24_m_max: Fix24_m_min;
- a.range_error(q);
- }
- else // standard shift-based division alg
- {
- q = 0;
- long r = la;
- for (int i = 31; i > 0; i--)
- {
- q <<= 1;
- r <<= 1;
- if (r >= 0)
- r -= lb;
- else
- r += lb;
- if (r >= 0)
- q |= 1;
- }
- if (r < 0) r += lb;
- if (r >= lb / 2) ++q;
- if (apos != bpos) q = -q;
- }
- return q;
- }
-
- Fix48 operator + (Fix48& f, Fix48& g)
- {
- long lo_r = (f.m.l >> 8) + (g.m.l >> 8);
- twolongs r = {
- f.m.u + g.m.u + (lo_r & 0x01000000L ? 0x00000100L : 0),
- lo_r << 8
- };
- if ( (f.m.u ^ r.u) & (g.m.u ^ r.u) & Fix24_msb )
- f.overflow(r);
- return r;
- }
-
- Fix48 operator - (Fix48& f, Fix48& g)
- {
- long lo_r = (f.m.l >> 8) - (g.m.l >> 8);
- twolongs r = {
- f.m.u - g.m.u - (lo_r & 0x01000000L ? 0x00000100L: 0),
- lo_r << 8
- };
- if ( (f.m.u ^ r.u) & (-g.m.u ^ r.u) & Fix24_msb )
- f.overflow(r);
- return r;
- }
-
- Fix48 operator * (Fix48& a, int b)
- {
- twolongs r;
- int bpos = (b >= 0);
- unsigned ub = (bpos)? b : -b;
- if ( ub >= 65536L ) {
- r = (bpos)? Fix48_m_max : Fix48_m_min;
- a.range_error(r);
- }
- else {
- unsigned long
- lo_r = (a.m.l & 0xffff) * ub,
- mi_r = ((a.m.l >> 16) & 0xffff) * ub,
- hi_r = a.m.u * ub;
- r.l = lo_r + (mi_r << 16);
- r.u = hi_r + ((mi_r >> 8) & 0x00ffff00L);
- if ( !bpos ) {
- unsigned long l = r.l;
- r.l = -r.l;
- r.u = ~r.u + ((l ^ r.l) & Fix24_msb ? 0 : Fix24_lsb);
- }
- }
- return r;
- }
-
- Fix48 operator << (Fix48& a, int b)
- {
- twolongs r = { 0L, 0L };
- if ( b >= 0 )
- if ( b < 24 ) {
- r.u = (a.m.u << b) + ((a.m.l >> (24 - b)) & 0xffffff00L);
- r.l = a.m.l << b;
- }
- else if ( b < 48 ) {
- r.u = a.m.l << (b - 24);
- }
- return r;
- }
-
- Fix48 operator >> (Fix48& a, int b)
- {
- twolongs r = { 0L, 0L };
- if ( b >= 0 )
- if ( b < 24 ) {
- r.l = (a.m.u << (24 - b)) + ((a.m.l >> b) & 0xffffff00L);
- r.u = (a.m.u >> b) & 0xffffff00L;
- }
- else if ( b < 48 ) {
- r.l = (a.m.u >> (b - 24)) & 0xffffff00L;
- r.u = (a.m.u >> 24) & 0xffffff00L;
- }
- else {
- r.l = (a.m.u >> 24) & 0xffffff00L;
- r.u = r.l;
- }
- return r;
- }
-
- // error handling
-
- void Fix24::overflow(long& i)
- {
- (*Fix24_overflow_handler)(i);
- }
-
- void Fix48::overflow(twolongs& i)
- {
- (*Fix48_overflow_handler)(i);
- }
-
- void Fix24::range_error(long& i)
- {
- (*Fix24_range_error_handler)(i);
- }
-
- void Fix48::range_error(twolongs& i)
- {
- (*Fix48_range_error_handler)(i);
- }
-
- // data definitions
-
- Fix24_peh Fix24_overflow_handler = Fix24_overflow_saturate;
- Fix48_peh Fix48_overflow_handler = Fix48_overflow_saturate;
-
- Fix24_peh Fix24_range_error_handler = Fix24_warning;
- Fix48_peh Fix48_range_error_handler = Fix48_warning;
-
- //function definitions
-
- Fix24_peh set_Fix24_overflow_handler(Fix24_peh new_handler) {
- Fix24_peh old_handler = Fix24_overflow_handler;
- Fix24_overflow_handler = new_handler;
- return old_handler;
- }
-
- Fix48_peh set_Fix48_overflow_handler(Fix48_peh new_handler) {
- Fix48_peh old_handler = Fix48_overflow_handler;
- Fix48_overflow_handler = new_handler;
- return old_handler;
- }
-
- void set_overflow_handler(Fix24_peh handler24, Fix48_peh handler48) {
- set_Fix24_overflow_handler(handler24);
- set_Fix48_overflow_handler(handler48);
- }
-
- Fix24_peh set_Fix24_range_error_handler(Fix24_peh new_handler) {
- Fix24_peh old_handler = Fix24_range_error_handler;
- Fix24_range_error_handler = new_handler;
- return old_handler;
- }
-
- Fix48_peh set_Fix48_range_error_handler(Fix48_peh new_handler) {
- Fix48_peh old_handler = Fix48_range_error_handler;
- Fix48_range_error_handler = new_handler;
- return old_handler;
- }
-
- void set_range_error_handler(Fix24_peh handler24, Fix48_peh handler48) {
- set_Fix24_range_error_handler(handler24);
- set_Fix48_range_error_handler(handler48);
- }
-
- void Fix24_overflow_saturate(long& i)
- { i = (i > 0 ? Fix24_m_min : Fix24_m_max); }
- void Fix24_ignore(long&) {}
- void Fix24_warning(long&)
- { cerr << "warning: Fix24 result out of range\n"; }
- void Fix24_overflow_warning_saturate(long& i)
- { cerr << "warning: Fix24 result out of range\n";
- Fix24_overflow_saturate(i); }
- void Fix24_abort(long&)
- { cerr << "error: Fix24 result out of range\n"; abort(); }
-
- void Fix48_ignore(twolongs&) {}
- void Fix48_overflow_saturate(twolongs& i)
- { i = (i.u > 0 ? Fix48_m_min : Fix48_m_max); }
- void Fix48_warning(twolongs&)
- { cerr << "warning: Fix48 result out of range\n"; }
- void Fix48_overflow_warning_saturate(twolongs& i)
- { cerr << "warning: Fix48 result out of range\n";
- Fix48_overflow_saturate(i); }
- void Fix48_abort(twolongs&)
- { cerr << "error: Fix48 result out of range\n"; abort(); }
-
-
-