// include/fixed.hh // This file is part of libpbe; see http://svn.chezphil.org/libpbe/ // It is also offered as a candidate for inclusion in Boost, http://boost.org/ // (C) 2007 Philip Endecott // This file is distributed under the terms of the Boost Software License v. 1.0, // see http://www.boost.org/LICENSE_1_0.txt. // Introduction // ------------ // This file provides class templates 'fixed' and 'ufixed' which implement signed and // unsigned fixed-point arithmetic. For background information about fixed-point // arithmetic, see for example this Wikipedia article: // http://en.wikipedia.org/wiki/Fixed-point_arithmetic. Note in particular that this // is binary arithmetic, not binary coded decimal. // // The author's requirement for fixed-point arithmetic is based on these // characteristics: // // - Comparing a 32-bit fixed-point value and a 32-bit single-precision // floating-point value, the fixed-point value has about 8 bits more precision // because it does not "waste" bits representing the exponent. In the case of // latitude and longitude values this makes the difference between a precision of // hundreds of metres and ones of metres. // // - Certain operations that directly operate on the bit patterns of values are much // simpler to carry out on fixed-point values than on floating-point values. (For // example, the bit interleaving required for Z-curves.) // // Other characteristics that are often beneficial include: // // - Computation is faster on systems that have no floating-point hardware, e.g. // embedded systems. // // - The precision of the values is well-defined and predictable. // // // Quick Start // ----------- // // Here's a very quick illustration of how the class can be used: // // #include // // void test() { // fixed<15,16> f1, f2; // Signed fixed-point values with 15 bits of integer part // // and 16 bits of fractional part. // f1 = 1.2345; // Conversion from floating point. // f2 = f1 - 2; // Mixed arithmetic with integers. // f2 = f1 / f2; // Arithmetic on fixed-point values. // } // // // Primary Objective // ----------------- // // The primary objective of this implementation is zero performance overhead compared // to using integer arithmetic with manually-inserted scaling. This is because // performance is important in many applications of fixed-point arithmetic and it // really isn't very hard to do ad-hoc fixed-point arithmetic using integers and // shifts. Users will therefore not be attracted to this library unless it can offer // this feature. // // // Secondary Objectives // -------------------- // // Secondary objectives are: // // - To behave in as many respects as possible in the same was as the built-in // numeric types (integer and floating-point). The motivation for this is to give // the user the behaviour that they are already familiar with from those types. // // // Hardware Support // ---------------- // // The objective of this implementation is to work efficiently with conventional // processors' integer arithmetic instructions. It is worth noting, however, that // some processors have hardware support for fixed-point operations. These include: // // [Hmm, no MMX doesn't seem to have any explicit fixed-point support] // // - DSP processors. // - Intel's MMX instruction set (also implemented in other x86-compatible // processors). // - The PowerPC's AltiVec instruction set. // - The ARM Piccolo instruction set. // // Support for any of these instruction sets would require either use of // architecture-specific assembly language or a very smart compiler. These are // considered outside the scope of the current work; however, it is worthwhile to see // what these instruction sets offer in order to avoid unintentionally implementing // something that is incompatible. // // For example, it seems that these instruction sets could more easily support // fixed<16,15> than fixed<15,16>. It may be that the user would be happy with // either of these formats. So we should consider offering a way to get the "best" // format with at least a certain number of bits of integer and fractional parts for // a particular instruction set. // // // Related Work // ------------ // // "Embedded C" (see ISO/IEC [draft] technical report 18037:2004) proposes fixed // point types for C. It defines // new keywords _Fract and _Accum which modify the built-in short, int and long // types; _Fract makes the value entirely fractional while _Accum gives it 4 // integer bits. // // It requires saturating arithmetic (controlled by an _Sat keyword or a #pragma), // and does not require wrap-around behaviour. // // Fixed-point constants can be written using letter suffixes, e.g. 3.1415uhk. // // It looks very much as if this proposal is motivated by one particular category of // applications (i.e. digital signal processing). The requirement for saturating // arithmetic imposes a significant overhead on processors that do not have support // for it, and the lack of support for values with more than 4 integer bits makes it // useless for the author's latitude/longitude application. // // // Getting the High part of a multiply result // ------------------------------------------ // // MMX has "packed multiply high" instruction. // // // Meaning of ++ and -- // -------------------- // // It could be argued that the meaning of ++ and -- is unclear: do they add/subtract // 1, or do they add/subtract the type's delta? (...there is an example of something // where they do delta...). I note, however, that the built-in floating point types // add and subtract 1, as does the proposed "Embedded C" fixed-point type. // Therefore, ++ and -- are defined to do that for fixed and ufixed. // // // Fixed Point Constants // --------------------- // // It would be ideal if code such as // // const ufixed<2,14> pi = 3.1415926; // // could have no run-time overhead. To be investigated... // // // Result Types // ------------ // // // Free Functions // -------------- // // embedded C has... // ...names are crytic due to absence of template/overloading in C. // // // Overflow and Saturation // ----------------------- // // Users of fixed-point arithmetic sometimes want to detect overflow, or for // operations to saturate when overflow would occur. However, users of // floating-point and integer arithmetic also sometimes want these features, but they // are not provided by the built-in C/C++ types. The approach taken in this // implementation is to follow the behaviour of the built-in types, i.e. to not // handle overflow; values will wrap-around. // // Hardware overhead of saturation; MMX has saturating instructions. // // // Division By Zero // ---------------- // // compare with int and float... // // embedded C fixed is undefined // // // Formatted input/output // ---------------------- // // ... implemented by conversion to floating point? ... // // // 'Math library' functions // ------------------------ // // A set of functions similar to those provided by the standard library for integer // and floating-point types is provided. The skeleton for these functions was // provided by ...... // // In the case of ....., tables of constants are required. Different tables are // required for each fixed point type. By default, the approach taken is to compute // these tables when the functions are first used [note that the use of static // variables may not be thread safe]. This could impose an unacceptable delay in // some applications; in this case, the preprocessor symbol ... should be defined, // and the included program ... should be run once for each type to generate static // tables. Simply include the output of this program in your build. // // // bigint // ------ // // // Benchmarks // ---------- // // The following results are based on the benchmark programs in the ... directory. // There are: // // - sort: generates a std::vector of xxxx pseudo-random values and sorts them // using std::sort. This measures comparison performance, which should be of the // same order as addition and subtraction. // // - filt: generates a vector of pseudo-random values and filters them using an FIR // (finite impulse response) filter. The filter coefficients are also generated // pseudo-randomly. This measures primarily multiply performance. // // In each case the performance is measured for fixed-point, floating-point and // integer types with manually-inserted shifts. // // The following platforms have been tested: // // - NSLU2. (See http://nslu2-linux.org/). This system has a 266 MHz Intel IXP420 // XScale ARM processor, which has no floating point hardware. The compiler used was // g++ verson 4.1.2. // // - The author's desktop computer, which as a 1 GHz VIA C3 (x86-compatible) // processor. The compiler used was also g++ version 4.1.2. // // Note to Boost people: I will change all the 'libpbe' identifiers to 'boost' // when this is formally proposed for review. #ifndef libpbe_fixed_hh #define libpbe_fixed_hh #include // for ldexp* #include "./integer.hpp" // Copy of boost::integer to which I've added 64-bit support. // I expect this functionality to be added to the official // version before this code is reviewed. #include #include namespace pbe { // This helper function exists mainly to avoid warnings about negative shifts. template T shift(T val) { if (amt>0) { unsigned int u_amt = amt; return val>>u_amt; } else { unsigned int u_amt = -amt; return val<::least> // WB = Whole Bits // FB = Fraction Bits // VAL_T = type to use for implementation class fixed: boost::totally_ordered >, boost::additive >, boost::bitwise >, boost::unit_steppable > { // Question: do we want to expose the implementation type and value? // In my application I have found it useful to do so. //private: public: typedef VAL_T val_t; val_t val; public: fixed() {} fixed(const fixed& x): val(x.val) {} template fixed(const fixed& x): val(shift(x.val)) //(X_FB>(X_FB-FB))) {} fixed(int x): val(x<(ldexpf(x,FB))) {} fixed(double x): val(static_cast(ldexp (x,FB))) {} operator float() const { return ldexpf(static_cast(val),-FB); } operator double() const { return ldexp (static_cast(val),-FB); } bool operator<(const fixed& x) const { return val fixed& operator*=(const fixed& x) { (*this) = (*this) * x; return *this; } template fixed& operator/=(const fixed& x) { (*this) = (*this) / x; return *this; } fixed& operator*=(float x) { (*this) = static_cast(*this) * x; return *this; } fixed& operator/=(float x) { (*this) = static_cast(*this) / x; return *this; } }; // I don't know how to choose the VAL_T for the results of these operations. // I.e. if the operands use safe_int then the result should also use safe_int. template pbe::fixed::value, boost::static_unsigned_max::value> operator+(pbe::fixed x, pbe::fixed y) { typedef pbe::fixed::value, boost::static_unsigned_max::value> result_t; result_t x_(x); result_t y_(y); return x_ + y_; } template pbe::fixed::value, boost::static_unsigned_max::value> operator-(pbe::fixed x, pbe::fixed y) { typedef pbe::fixed::value, boost::static_unsigned_max::value> result_t; result_t x_(x); result_t y_(y); return x_ - y_; } template pbe::fixed operator*(pbe::fixed x, pbe::fixed y) { typedef pbe::fixed result_t; result_t r; r.val = static_cast(x.val) * y.val; return r; } template pbe::fixed operator/(pbe::fixed x, pbe::fixed y) { pbe::fixed r; r.val = x.val; r.val <<= (YWB+YFB); r.val /= y.val; return r; } template float operator*(pbe::fixed x, float y) { // fixed * float is done using floating-point. Right choice? return static_cast(x) * y; } template float operator/(pbe::fixed x, float y) { // Ditto. return static_cast(x) / y; } // Also planned: an unsigned version. }; #endif