"P.J. Plauger" <pj*@dinkumware .com> writes:
"Roger Leigh" <${******@inval id.whinlatter.u klinux.net.inva lid> wrote in message
news:87******** ****@wrynose.wh inlatter.uklinu x.net...
To output a number, I was manually splitting up the number into whole
and fractional parts and processing them separately, using '.' as the
decimal point symbol. However, I've just discovered the existence of
std::locale::nu meric and std::locale::mo netary locale facets, and the
num_put() and num_get() methods. Ideally, I'd like to use these
functions for the the conversions (FixedFloat -> std::string).
However, their support for the standard numeric types (int, long,
float, double) is hard-coded into the class. I can't risk conversion
to a supported type such as double, due to loss of precision (0.60
would becomes 0.59 on my i686-pc-linux-gnu arch), and they will be
used to process financial data!
Is it possible to extend these to support my FixedFloat class?
It's possible, but probably not a rewarding exercise.
If I'm correct here, I would have to add my own custom locale facet(s)
to allow this, but I'd need to do this manually for each locale I want
to use, which would be a pain.
[do_put() and __convert_from_ v()] The do_put part is portable, the other isn't. But you're right to
observe that they're hideously complex.
OK.
Lastly, are there any standard classes that do this sort of thing?
You can pervert money_put and moneypunct to output a digit sequence
stored in a string, with a specified number of decimal places, commas
between thousands groups, etc.
This looks like what I'll do. I'll derive a "Money" class from
FixedFloat and do that in there, overriding the standard ostream<< and
istream>> operators.
I've attached a copy of the working class, and a small driver program
to show it in action (sorry it's so long). I have a few questions
about this:
1. Is the header file OK style-wise? Is there anything wrong that I
should not be doing?
2. I've noticed that the modulus (operator%) member and friend
functions can be out by a small factor e.g. 0.0001 in a 4
d.p. precision class. With fixed-point arithmetic, should I be
doing anything to correct this? Is it actually incorrect? For
some reason, I couldn't get the "real" % operator to work, so had
to resort to the hack that actually gets used (subtracting the
result of division and subsequent multiplication from the original
value).
3. Looking at the compiled binary, the FixedFloat symbols have weak,
rather than vague linkage. I thought that /all/ templated class
methods and functions would be vague. This is with GCC 3.3.2, GNU
ld 2.14.90.0.6 and binutils 2.14.90.0.6-5 on i686-pc-linux-gnu
using ELF binary format (this is probably OT).
4. In the stream output and extraction friend classes, is the use of
locales correct? I've not used locales (in C++) before, and I've
done this using the Josuttis Standard Library book.
Many thanks for your time,
Roger
----begin main.cc----
#include <iostream>
#include "fixedfloat .h"
int main()
{
std::locale::gl obal(std::local e(""));
std::cout.imbue (std::locale()) ;
FixedFloat<4> f1("4.1246");
FixedFloat<4> f2("2.3443");
std::cout << f1 << std::endl;
std::cout << f2 << std::endl;
FixedFloat<4> n(f1);
FixedFloat<4> o;
o = f2;
std::cout << n << std::endl;
std::cout << o << std::endl;
std::cout << "Signedness \n";
std::cout << +f1 << std::endl;
std::cout << -f1 << std::endl;
std::cout << "Binary arithmetic\n";
std::cout << f1 << "-" << f2 << "=" << f1-f2 << "\n";
std::cout << f1 << "+" << f2 << "=" << f1+f2 << "\n";
std::cout << f1 << "*" << f2 << "=" << f1*f2 << "\n";
std::cout << f1 << "/" << f2 << "=" << f1/f2 << "\n";
std::cout << f1 << "%" << f2 << "=" << f1%f2 << "\n";
std::cout << -f1 << "/" << f2 << "=" << (-f1)/f2 << "\n";
std::cout << -f1 << "%" << f2 << "=" << (-f1)%f2 << "\n";
std::cout << "Logic\n";
std::cout << f1 << "==" << f1 << "=" << (f1==f1) << "\n";
std::cout << f1 << "==" << f2 << "=" << (f1==f2) << "\n";
std::cout << f1 << "!=" << f1 << "=" << (f1!=f1) << "\n";
std::cout << f1 << "!=" << f2 << "=" << (f1!=f2) << "\n";
std::cout << "Unary arithmetic\n";
FixedFloat<4> f3 = f1;
f3 += FixedFloat<4>(" 2.3430");
std::cout << f1 << "+=2.3430" << "=" << f3 << "\n";
f3 = f1;
f3 -= FixedFloat<4>(" 2.3430");
std::cout << f1 << "-=2.3430" << "=" << f3 << "\n";
f3 = f1;
f3 *= FixedFloat<4>(" 2.3430");
std::cout << f1 << "*=2.3430" << "=" << f3 << "\n";
f3 = f1;
f3 /= FixedFloat<4>(" 2.3430");
std::cout << f1 << "/=2.3430" << "=" << f3 << "\n";
f3 = f1;
f3 %= FixedFloat<4>(" 2.3430");
std::cout << f1 << "%=2.3430" << "=" << f3 << "\n";
f3 = f1;
++f3;
std::cout << "++" << f1 << "=" << f3 << "\n";
f3 = f1;
std::cout << f1 << "++" << "=" << f3++ << " (before)\n";
std::cout << f1 << "++" << "=" << f3 << " (after)\n";
f3 = f1;
--f3;
std::cout << "--" << f1 << "=" << f3 << "\n";
f3 = f1;
std::cout << f1 << "--" << "=" << f3-- << " (before)\n";
std::cout << f1 << "--" << "=" << f3 << " (after)\n";
return 1;
}
----end main.cc----
----begin fixedfloat.h----
// fixed floating point class -*- C++ -*-
// $Id: template.cc,v 1.1 2003/09/14 21:56:55 roger Exp $
//
// Copyright (C) 2003 Roger Leigh.
//
// Authors: Roger Leigh <ro***@whinlatt er.uklinux.net>
#include <iomanip>
#include <istream>
#include <locale>
#include <ostream>
#include <sstream>
/**
* A class to represent fixed floating point numbers with high
* accuracy.
* The float and double data types to not offer enough accuracy when
* dealing with some types of data, for example currency values, since
* they cannot garuantee that a particular value is representable in
* their floating-point binary format. This class will garuantee
* accuracy, with the restriction that there is a fixed number of
* decimal places after the decimal point. Internally, the value is
* held as a long integer.
*
* Conversion to and from the double data type is not implicit--this
* must be done using the methods provided. However, conversion to
* and from std::string is possible.
*/
template <size_t _decimal_places = 2>
class FixedFloat {
public:
/// The type used internally to hold fixed floating point values.
typedef long int value_type;
private:
/// The integer value.
value_type m_value;
/// The correction factor.
value_type m_correction;
/**
* Compute the correction factor.
* The correction value is used to correct multiplication and
* division of fixed point numbers.
*/
void compute_correct ion()
{
m_correction = 1;
for (int i = 0; i < _decimal_places ; ++i)
m_correction *= 10;
}
/**
* The constructor.
* The initial value is set to the value provided.
* @param value the initial value.
*/
FixedFloat(valu e_type value):
m_value(value)
{
compute_correct ion();
}
public:
/**
* The constructor.
* The initial value is set to 0.
*/
FixedFloat():
m_value(0)
{
compute_correct ion();
}
/**
* The constructor.
* The initial value is set to the value provided. If there are too
* many numbers after the decimal place, they will be rounded to the
* nearest representable value (0 to 4 are rounded down, 5 to 9 are
* rounded up.
* @param value the initial value.
*/
FixedFloat(cons t std::string& value)
{
compute_correct ion();
std::istringstr eam input(value);
input >> *this;
}
/**
* The copy constructor.
*/
FixedFloat(cons t FixedFloat& original):
m_value(origina l.m_value),
m_correction(or iginal.m_correc tion)
{}
/// The destructor.
~FixedFloat()
{}
FixedFloat& operator = (const FixedFloat& rhs)
{
m_value = rhs.m_value;
return *this;
}
FixedFloat& operator += (const FixedFloat& rhs)
{
m_value += rhs.m_value;
return *this;
}
FixedFloat& operator -= (const FixedFloat& rhs)
{
m_value -= rhs.m_value;
return *this;
}
FixedFloat& operator *= (const FixedFloat& rhs)
{
m_value *= rhs.m_value;
m_value /= m_correction;
return *this;
}
FixedFloat& operator /= (const FixedFloat& rhs)
{
m_value *= m_correction;
m_value /= rhs.m_value;
return *this;
}
FixedFloat& operator %= (const FixedFloat& rhs)
{
*this = (*this - ((*this / rhs) * rhs));
return *this;
}
FixedFloat& operator ++ ()
{
m_value += m_correction;
return *this;
}
FixedFloat operator ++ (int)
{
FixedFloat ret(*this);
m_value += m_correction;
return ret;
}
FixedFloat& operator -- ()
{
m_value -= m_correction;
return *this;
}
FixedFloat operator -- (int)
{
FixedFloat ret(*this);
m_value -= m_correction;
return ret;
}
friend FixedFloat<_dec imal_places> operator +<> (const FixedFloat<_dec imal_places>& lhs,
const FixedFloat<_dec imal_places>& rhs);
friend FixedFloat<_dec imal_places> operator -<> (const FixedFloat<_dec imal_places>& lhs,
const FixedFloat<_dec imal_places>& rhs);
friend FixedFloat<_dec imal_places> operator *<> (const FixedFloat<_dec imal_places>& lhs,
const FixedFloat<_dec imal_places>& rhs);
friend FixedFloat<_dec imal_places> operator /<> (const FixedFloat<_dec imal_places>& lhs,
const FixedFloat<_dec imal_places>& rhs);
friend FixedFloat<_dec imal_places> operator %<> (const FixedFloat<_dec imal_places>& lhs,
const FixedFloat<_dec imal_places>& rhs);
friend bool operator ==<> (const FixedFloat<_dec imal_places>& lhs,
const FixedFloat<_dec imal_places>& rhs);
friend bool operator !=<> (const FixedFloat<_dec imal_places>& lhs,
const FixedFloat<_dec imal_places>& rhs);
friend FixedFloat<_dec imal_places> operator -<> (const FixedFloat<_dec imal_places>& rhs);
friend FixedFloat<_dec imal_places> operator +<> (const FixedFloat<_dec imal_places>& rhs);
friend std::ostream& operator <<<> (std::ostream& output_stream,
const FixedFloat<_dec imal_places>& rhs);
friend std::istream& operator >><> (std::istream& input_stream,
FixedFloat<_dec imal_places>& rhs);
}; // class FixedFloat<>
// Friend functions.
template <size_t _decimal_places >
inline FixedFloat<_dec imal_places> operator + (const FixedFloat<_dec imal_places>& lhs,
const FixedFloat<_dec imal_places>& rhs)
{
return FixedFloat<_dec imal_places>(lh s.m_value + rhs.m_value);
}
template <size_t _decimal_places >
inline FixedFloat<_dec imal_places> operator - (const FixedFloat<_dec imal_places>& lhs,
const FixedFloat<_dec imal_places>& rhs)
{
return FixedFloat<_dec imal_places>(lh s.m_value - rhs.m_value);
}
template <size_t _decimal_places >
inline FixedFloat<_dec imal_places> operator * (const FixedFloat<_dec imal_places>& lhs,
const FixedFloat<_dec imal_places>& rhs)
{
return FixedFloat<_dec imal_places>((l hs.m_value * rhs.m_value) / lhs.m_correctio n);
}
template <size_t _decimal_places >
inline FixedFloat<_dec imal_places> operator / (const FixedFloat<_dec imal_places>& lhs,
const FixedFloat<_dec imal_places>& rhs)
{
return FixedFloat<_dec imal_places>((l hs.m_value * lhs.m_correctio n) / rhs.m_value);
}
template <size_t _decimal_places >
inline FixedFloat<_dec imal_places> operator % (const FixedFloat<_dec imal_places>& lhs,
const FixedFloat<_dec imal_places>& rhs)
{
return FixedFloat<_dec imal_places>(lh s - ((lhs / rhs) * rhs));
}
template <size_t _decimal_places >
inline bool operator == (const FixedFloat<_dec imal_places>& lhs,
const FixedFloat<_dec imal_places>& rhs)
{
return lhs.m_value == rhs.m_value;
}
template <size_t _decimal_places >
inline bool operator != (const FixedFloat<_dec imal_places>& lhs,
const FixedFloat<_dec imal_places>& rhs)
{
return lhs.m_value != rhs.m_value;
}
template <size_t _decimal_places >
inline FixedFloat<_dec imal_places> operator - (const FixedFloat<_dec imal_places>& rhs)
{
return FixedFloat<_dec imal_places>(-rhs.m_value);
}
template <size_t _decimal_places >
inline FixedFloat<_dec imal_places> operator + (const FixedFloat<_dec imal_places>& rhs)
{
return FixedFloat<_dec imal_places>(rh s.m_value);
}
template <size_t _decimal_places >
inline std::ostream& operator << (std::ostream& output_stream,
const FixedFloat<_dec imal_places>& rhs)
{
bool negative = false;
if (rhs.m_value < 0)
negative = true;
typename FixedFloat<_dec imal_places>::v alue_type whole_part
= rhs.m_value / rhs.m_correctio n;
typename FixedFloat<_dec imal_places>::v alue_type fractional_part
= rhs.m_value - (whole_part * rhs.m_correctio n);
if (whole_part < 0) // turn into a positive number
whole_part = -whole_part;
if (fractional_par t < 0) // turn into a positive number
fractional_part = -fractional_part ;
std::ostringstr eam s;
s.copyfmt(outpu t_stream);
s.width(0);
if (negative == true)
s << '-'; // output sign, if needed
s << whole_part; // output the whole part
if (_decimal_place s > 0) // output the fractional part, including decimal point
{
std::ostringstr eam fractional_stri ng;
fractional_stri ng.imbue(std::l ocale::classic( )); // "plain" numbers
fractional_stri ng << fractional_part ;
s << std::use_facet< std::numpunct<c har> >(s.getloc()).d ecimal_point()
<< std::setw(_deci mal_places) << std::setfill('0 ')
<< fractional_stri ng.str();
}
output_stream << s.str();
return output_stream;
}
template <size_t _decimal_places >
inline std::istream& operator >> (std::istream& input_stream,
FixedFloat<_dec imal_places>& rhs)
{
bool negative = false;
typename FixedFloat<_dec imal_places>::v alue_type whole_part = 0;
char decimal_point;
typename FixedFloat<_dec imal_places>::v alue_type fractional_part = 0;
std::istream::s entry stream_sentry(i nput_stream, true);
if (stream_sentry)
{
// Get the whole part of the number
if (input_stream.b ad())
return input_stream;
// Check signedness (would be lost if value is < 1, since -0 == 0)
if (input_stream.p eek() == '+')
negative = false;
else if (input_stream.p eek() == '-')
negative = true;
input_stream >> whole_part;
whole_part *= rhs.m_correctio n;
if (whole_part < 0) // turn into a positive number
whole_part = -whole_part;
if (_decimal_place s > 0)
{
// Get the decimal point.
if (input_stream.b ad())
return input_stream;
input_stream >> decimal_point;
// Check that the decimal point was the correct type for this locale
if (decimal_point != std::use_facet< std::numpunct<c har> >(input_stream. getloc()).decim al_point())
{
rhs.m_value = 0;
input_stream.se tstate(std::ios ::failbit);
}
// Get the fractional part of the number
fractional_part = 0;
for (size_t i = _decimal_places ; i > 0; --i)
{
if (input_stream.b ad())
return input_stream;
char decimal_char = '0';
input_stream >> decimal_char;
if (decimal_char < '0' || decimal_char > '9')
{
rhs.m_value = 0;
input_stream.se tstate(std::ios ::failbit);
return input_stream;
}
size_t decimal_number = decimal_char - '0';
size_t multiply_factor = 1;
for (int j = 1; j < i; ++j)
multiply_factor *= 10;
fractional_part += (decimal_number * multiply_factor );
}
}
if (negative == false)
rhs.m_value = whole_part + fractional_part ;
else
rhs.m_value = - (whole_part + fractional_part );
}
else
input_stream.se tstate(std::ios ::failbit);
return input_stream;
}
----end fixedfloat.h----
--
Roger Leigh
Printing on GNU/Linux?
http://gimp-print.sourceforge.net/
GPG Public Key: 0x25BFB848. Please sign and encrypt your mail.