Topic: Fixed-point class challenge--Needs explicit conversion op!


Author: sean@delta.com (Sean L. Palmer)
Date: 1996/10/16
Raw View
Here is a stripped-down version of my Fixed-point class.  Total is about
100 lines (sorry!)

IF anyone here can find a good way to use this class interchangeably
with double in a template, WITHOUT requiring specially-named conversion
functions (the method it uses now) to be provided for double (in effect
requiring the template be written for THIS class, and just happen to
work for double as well), and without forcing all conversions to be
handled through operator double() const which involves way too much
runtime overhead when converting to int, or operator int() const which
loses accuracy when converting to double, and without providing it with
a full set of every known numeric type in the known and as-yet-unknown
universe (what about new __int64 types?) as conversion operators, I will
retract my proposal for adding explicit conversion operators to the C++
language standard.

I don't think it can be done properly without adding explicit conversion
operators to the language.

So if anyone can modify this so that it's standard-conforming and
satisfies the above constraints (interchangeable with double in any
well-written template that was written with float/double/long double in
mind, especially in the presence of typecasts to/from int types, doesn't
force all conversions to be through conversion to one particular type,
and without providing separate conversion operators for every kind and
flavor of scalar and float type), then it will prove to me that explicit
conversion operators are unnecessary.

I just ask that you prove it to yourselves.

Crap! I'm having a hard time figuring out which directory on my HD has
the latest version of this. This may have minor problems. It's a hacked
up version of a version I'm not sure is current. If it does have
problems, you can probably figure it out, but notify me and I'll correct
it.

Feel free to redistribute this or post it or put it in an archive.

//  Fixed Point Class
//  Copyright 1996 Sean L. Palmer  (mailto:sean@delta.com)
(http://delta.com/sean)
//  Freely distributable!
//  This copyright must remain in any redistribution
//  Derivative works must give appropriate credit.

#ifndef __fixpt_h
#define __fixpt_h

#define FIXEDPOINT

#include <math.h>   //for sqrt  (this should be <cmath>)
#include <stdlib.h> //for abs   (this should be <cstdlib>)


typedef unsigned int uint;

#ifndef __STD_UTILITY__ //in case someone included the Rogue Wave STL
first
//with these, any relational operator can be simulated if the
//class has just == and <
template <class T>
  inline bool operator > (const T& a,const T& b) { return  (b < a); }
template <class T>
  inline bool operator >=(const T& a,const T& b) { return !(a < b); }
template <class T>
  inline bool operator <=(const T& a,const T& b) { return !(b < a); }
template <class T>
  inline bool operator !=(const T& a,const T& b) { return !(a ==b); }
#endif

#ifndef fixsize
  enum { fixsize=16 };
#endif

//dunno what compiler this is, can't use inline asm, so fake it using C

//here is where some standard long long int (64-bit) routines would be
handy.
#if 0  //with loss of precision
  inline long _scale(long f,long m,long d) { return f * m / d; }
  inline long _fmul(long f,long m,char bits) { return (f >>
(fixsize>>1)) * (m >> (fixsize>>1)); }
  inline long _fdiv(long f,long d,char bits) { return (f / (m >>
(fixsize>>1))) << (fixsize>>1); }
#else  //with loss of speed
  inline long _scale(long f,long m,long d) { return long(f * double(m) /
d); }
  inline long _fmul(long f,long m,char bits) { return
long(f*double(m)/(1<<fixsize)); }
  inline long _fdiv(long f,long d,char bits) { return
long(double(f)*(1<<fixsize)/d); }
#endif

class fix {
  typedef fix F;
  typedef const fix& CFR;
protected:
  enum { pnt=1L<<fixsize };
  enum { mask=pnt-1 };

  long v; //value * fixsize
  fix(long l, char) { v = l; }
  static inline F Fix(long l) { return F(l,'\0'); }
public:
  fix(int i=0) { v = i<<fixsize; }
  fix(double d) { v = long(d*pnt); }
  //copy ctor and assignment are automatically generated
  //but Watcom makes LOUSY code, so we do it ourselves.
  fix(CFR f) : v(f.v) {}
  F& operator =(CFR f) { v=f.v; return *this; }

  //this class would work MUCH better if the ANSI C++ committee
  //would add explicit conversion operators similar to the
  //new explicit constructors...
  //operator int() const { return v>>fixsize; } //too damn dangerous.
  //operator double() const { return v/pnt; } //too damn slow.
  long Long() const { return v; }
  int  Int () const { return v>>fixsize; }
  char Char() const { return char(v>>fixsize); }
  int  Bool() const { return v; }
  double Double() const { return double(v)/pnt; }

  friend inline long Long(CFR f) { return f.Long(); }
  friend inline int  Int (CFR f) { return f.Int(); }
  friend inline char Char(CFR f) { return f.Char(); }
  friend inline int  Bool(CFR f) { return f.Bool(); }
  friend inline double Double(CFR f) { return f.Double(); }
  friend inline F frac(CFR f) { return f.frac(); }
  friend inline F invfrac(CFR f) { return f.invfrac(); }
  friend inline int fractional(CFR f) { return f.v & mask; }

  F inv() const { return Fix(_finv(v)); }

  F  operator -() const { return Fix(-v); }
  int operator !() const { return !v; }

  F& operator ++() { v+=pnt; return *this; }
  F& operator --() { v-=pnt; return *this; }
  F  operator ++(int) { F t(*this); v+=pnt; return t; }
  F  operator --(int) { F t(*this); v-=pnt; return t; }
  F& operator +=(CFR f) { v+=f.v; return *this; }
  F& operator -=(CFR f) { v-=f.v; return *this; }

  F& operator *=(int i) { v*=i; return *this; }
  F& operator *=(CFR f) { v=_fmul(v,f.v); return *this; }
  F& operator /=(int i) { v/=i; return *this; }
  F& operator /=(CFR f) { v=_fdiv(v,f.v); return *this; }

  F& operator <<=(unsigned n) { v <<= n; return *this; }
  F& operator >>=(unsigned n) { v >>= n; return *this; }

  friend inline F operator +(CFR l, CFR r) { return F(l) += r; }
  friend inline F operator +(int l, CFR r) { return F(l) += r; }
  friend inline F operator +(CFR l, int r) { return F(l) += r; }
  friend inline F operator -(CFR l, CFR r) { return F(l) -= r; }
  friend inline F operator -(int l, CFR r) { return F(l) -= r; }
  friend inline F operator -(CFR l, int r) { return F(l) -= r; }
  friend inline F operator *(CFR l, CFR r) { return F(l) *= r; }
  friend inline F operator *(CFR l, int r) { return F(l) *= r; }
  friend inline F operator *(int l, CFR r) { return F(r) *= l; }
  friend inline F operator /(CFR l, CFR r) { return F(l) /= r; }
  friend inline F operator /(CFR l, int r) { return F(l) /= r; }
  friend inline F operator /(int l, CFR r) { return F(l) /= r; }
  friend inline F operator <<(CFR l,unsigned n) { return F(l) <<= n; }
  friend inline F operator >>(CFR l,unsigned n) { return F(l) >>= n; }

  friend inline int operator ==(CFR l, CFR r) { return l.v == r.v; }
  friend inline int operator ==(int l, CFR r) { return F(l) == r;  }
  friend inline int operator ==(CFR l, int r) { return l == F(r);  }
  friend inline int operator < (CFR l, CFR r) { return l.v < r.v; }
  friend inline int operator < (int l, CFR r) { return F(l) < r;  }
  friend inline int operator < (CFR l, int r) { return l <  F(r);  }
  //all the other comparisons are simulated with generic templates

  //generic neg function negates a number so that a temporary can be
avoided.
  friend inline void neg(F& f) { f.v=-f.v; }
  friend inline F abs(CFR f) { return F::Fix(abs(f.v)); }
  friend inline F sqr(CFR f) { return f*f; }
  friend inline F sqrt(CFR f) {
    if (f<0) return F::Fix((ulong)(-1)>>1); //most likely something
overflowed.
    return F(sqrt(::Double(f)));
  }
  friend inline F floor(CFR f) { return F::Fix(f.v & ~mask); }
  friend inline F ceil(CFR f) { return F::Fix((f.v+mask)& ~mask); }
  friend inline F shl(CFR a,int shift) { return a<<shift; }
  friend inline F shr(CFR a,int shift) { return a>>shift; }
  friend inline int negligible(CFR a) { return !a; }
  friend inline F fmul(CFR i,CFR j) { return i*j; }
  friend inline F fdiv(CFR n,CFR d) { return n*d; }
  friend inline F finv(CFR d) { return d.inv(); }
  friend inline F fscale(CFR i,CFR m,CFR d) { return i.scale(m,d); }
  friend inline F scale(CFR i,CFR m,CFR d) { return i.scale(m,d); }
};

#endif
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]