|
|
|
/***************************************************************************
|
|
|
|
mymoneymymoney.cpp - description
|
|
|
|
-------------------
|
|
|
|
begin : Thu Feb 21 2002
|
|
|
|
copyright : (C) 2000-2002 by Michael Edwardes
|
|
|
|
email : mte@users.sourceforge.net
|
|
|
|
Javier Campos Morales <javi_c@users.sourceforge.net>
|
|
|
|
Felix Rodriguez <frodriguez@users.sourceforge.net>
|
|
|
|
John C <thetacoturtle@users.sourceforge.net>
|
|
|
|
Thomas Baumgart <ipwizard@users.sourceforge.net>
|
|
|
|
Kevin Tambascio <ktambascio@users.sourceforge.net>
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
* it under the terms of the GNU General Public License as published by *
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
|
|
* (at your option) any later version. *
|
|
|
|
* *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
// make sure, that this is defined before we even include any other header file
|
|
|
|
#ifndef __STDC_LIMIT_MACROS
|
|
|
|
#define __STDC_LIMIT_MACROS // force definition of min and max values
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// QT Includes
|
|
|
|
|
|
|
|
#include <tqregexp.h>
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Project Includes
|
|
|
|
|
|
|
|
#include "mymoneymoney.h"
|
|
|
|
#include "mymoneyaccount.h"
|
|
|
|
#include "mymoneysecurity.h"
|
|
|
|
|
|
|
|
unsigned char MyMoneyMoney::_thousandSeparator = ',';
|
|
|
|
unsigned char MyMoneyMoney::_decimalSeparator = '.';
|
|
|
|
MyMoneyMoney::signPosition MyMoneyMoney::_negativeMonetarySignPosition = BeforeQuantityMoney;
|
|
|
|
MyMoneyMoney::signPosition MyMoneyMoney::_positiveMonetarySignPosition = BeforeQuantityMoney;
|
|
|
|
bool MyMoneyMoney::_negativePrefixCurrencySymbol = false;
|
|
|
|
bool MyMoneyMoney::_positivePrefixCurrencySymbol = false;
|
|
|
|
|
|
|
|
MyMoneyMoney::fileVersionE MyMoneyMoney::_fileVersion = MyMoneyMoney::FILE_4_BYTE_VALUE;
|
|
|
|
|
|
|
|
MyMoneyMoney MyMoneyMoney::maxValue = MyMoneyMoney(INT64_MAX,100);
|
|
|
|
MyMoneyMoney MyMoneyMoney::minValue = MyMoneyMoney(INT64_MIN,100);
|
|
|
|
MyMoneyMoney MyMoneyMoney::autoCalc = MyMoneyMoney(INT64_MIN+1,100);
|
|
|
|
|
|
|
|
void MyMoneyMoney::setNegativePrefixCurrencySymbol(const bool flag)
|
|
|
|
{
|
|
|
|
_negativePrefixCurrencySymbol = flag;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MyMoneyMoney::setPositivePrefixCurrencySymbol(const bool flag)
|
|
|
|
{
|
|
|
|
_positivePrefixCurrencySymbol = flag;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MyMoneyMoney::setNegativeMonetarySignPosition(const signPosition pos)
|
|
|
|
{
|
|
|
|
_negativeMonetarySignPosition = pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
MyMoneyMoney::signPosition MyMoneyMoney::negativeMonetarySignPosition(void)
|
|
|
|
{
|
|
|
|
return _negativeMonetarySignPosition;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MyMoneyMoney::setPositiveMonetarySignPosition(const signPosition pos)
|
|
|
|
{
|
|
|
|
_positiveMonetarySignPosition = pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
MyMoneyMoney::signPosition MyMoneyMoney::positiveMonetarySignPosition(void)
|
|
|
|
{
|
|
|
|
return _positiveMonetarySignPosition;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MyMoneyMoney::setThousandSeparator(const unsigned char separator)
|
|
|
|
{
|
|
|
|
if(separator != ' ')
|
|
|
|
_thousandSeparator = separator;
|
|
|
|
else
|
|
|
|
_thousandSeparator = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char MyMoneyMoney::thousandSeparator(void)
|
|
|
|
{
|
|
|
|
return _thousandSeparator;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MyMoneyMoney::setDecimalSeparator(const unsigned char separator)
|
|
|
|
{
|
|
|
|
if(separator != ' ')
|
|
|
|
_decimalSeparator = separator;
|
|
|
|
else
|
|
|
|
_decimalSeparator = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned char MyMoneyMoney::decimalSeparator(void)
|
|
|
|
{
|
|
|
|
return _decimalSeparator;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MyMoneyMoney::setFileVersion(fileVersionE version)
|
|
|
|
{
|
|
|
|
_fileVersion = version;
|
|
|
|
}
|
|
|
|
|
|
|
|
MyMoneyMoney::MyMoneyMoney(const TQString& pszAmount)
|
|
|
|
{
|
|
|
|
m_num = 0;
|
|
|
|
m_denom = 1;
|
|
|
|
|
|
|
|
// an empty string is zero
|
|
|
|
if (pszAmount.isEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// take care of prices given in the form "8 5/16"
|
|
|
|
// and our own internal represenation
|
|
|
|
TQRegExp regExp("^((\\d+)\\s+|-)?(\\d+)/(\\d+)");
|
|
|
|
// +-#2-+ +-#3-+ +-#4-+
|
|
|
|
// +-----#1-----+
|
|
|
|
if (regExp.search(pszAmount) > -1) {
|
|
|
|
m_num = regExp.cap(3).toLongLong();
|
|
|
|
m_denom = regExp.cap(4).toLongLong();
|
|
|
|
const TQString& part1 = regExp.cap(1);
|
|
|
|
if(!part1.isEmpty()) {
|
|
|
|
if(part1 == TQString("-")) {
|
|
|
|
m_num = -m_num;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
*this += MyMoneyMoney(regExp.cap(2));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString res = pszAmount;
|
|
|
|
// get rid of anything that is not
|
|
|
|
// a) numeric
|
|
|
|
// b) _decimalSeparator
|
|
|
|
// c) negative indicator
|
|
|
|
TQString validChars = TQString("\\d%1").arg(TQChar(decimalSeparator()));
|
|
|
|
// we need to escape the minus sign here, because later on it will be
|
|
|
|
// part of "\d,-()" and that does not work. It needs to be "\d,\-()"
|
|
|
|
// And we need two of them, because we're in C
|
|
|
|
TQString negChars("\\-");
|
|
|
|
if(_negativeMonetarySignPosition == ParensAround) {
|
|
|
|
// Since we want to allow '-' as well as '()' for negative entry
|
|
|
|
// we just add the parens here.
|
|
|
|
negChars += "()";
|
|
|
|
}
|
|
|
|
validChars += negChars;
|
|
|
|
// tqDebug("0: '%s'", validChars.data());
|
|
|
|
|
|
|
|
TQRegExp invChars(TQString("[^%1]").arg(validChars));
|
|
|
|
// tqDebug("1: '%s'", res.data());
|
|
|
|
res.remove(invChars);
|
|
|
|
|
|
|
|
TQRegExp negCharSet(TQString("[%1]").arg(negChars));
|
|
|
|
bool isNegative = false;
|
|
|
|
if(res.find(negCharSet) != -1) {
|
|
|
|
isNegative = true;
|
|
|
|
res.remove(negCharSet);
|
|
|
|
}
|
|
|
|
// tqDebug("2: '%s' %s", res.data(), isNegative ? "(-)" : "");
|
|
|
|
int pos;
|
|
|
|
|
|
|
|
// tqDebug("3: '%s'", res.data());
|
|
|
|
if((pos = res.find(_decimalSeparator)) != -1) {
|
|
|
|
// make sure, we get the denominator right
|
|
|
|
m_denom = precToDenom(res.length() - pos - 1);
|
|
|
|
|
|
|
|
// now remove the decimal symbol
|
|
|
|
res.remove(pos, 1);
|
|
|
|
}
|
|
|
|
// tqDebug("4: '%s'", res.data());
|
|
|
|
if(res.length() > 0)
|
|
|
|
m_num = atoll( res );
|
|
|
|
|
|
|
|
if(isNegative)
|
|
|
|
m_num = -m_num;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString MyMoneyMoney::formatMoney(int denom, bool showThousandSeparator) const
|
|
|
|
{
|
|
|
|
return formatMoney("", denomToPrec(denom), showThousandSeparator);
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString MyMoneyMoney::formatMoney(const MyMoneyAccount& acc, const MyMoneySecurity& sec, bool showThousandSeparator) const
|
|
|
|
{
|
|
|
|
return formatMoney(sec.tradingSymbol(), denomToPrec(acc.fraction()), showThousandSeparator);
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString MyMoneyMoney::formatMoney(const MyMoneySecurity& sec, bool showThousandSeparator) const
|
|
|
|
{
|
|
|
|
return formatMoney(sec.tradingSymbol(), denomToPrec(sec.smallestAccountFraction()), showThousandSeparator);
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString MyMoneyMoney::formatMoney(const TQString& currency, const int prec, bool showThousandSeparator) const
|
|
|
|
{
|
|
|
|
TQString res;
|
|
|
|
TQString tmpCurrency = currency;
|
|
|
|
int tmpPrec = prec;
|
|
|
|
signed64 denom = 1;
|
|
|
|
signed64 m_64Value;
|
|
|
|
|
|
|
|
// if prec == -1 we want the maximum possible but w/o trailing zeroes
|
|
|
|
if(tmpPrec > -1) {
|
|
|
|
while(tmpPrec--) {
|
|
|
|
denom *= 10;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// fix it to a max of 8 digits on the right side for now
|
|
|
|
denom = 100000000;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_64Value = convert(denom).m_num;
|
|
|
|
|
|
|
|
// Once we really support multiple currencies then this method will
|
|
|
|
// be much better than using KGlobal::locale()->formatMoney.
|
|
|
|
bool bNegative = false;
|
|
|
|
signed64 left = m_64Value / denom;
|
|
|
|
signed64 right = m_64Value % denom;
|
|
|
|
|
|
|
|
if (right < 0){
|
|
|
|
right = -right;
|
|
|
|
bNegative = true;
|
|
|
|
}
|
|
|
|
if (left < 0) {
|
|
|
|
left = -left;
|
|
|
|
bNegative = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(left & 0xFFFFFFFF00000000LL) {
|
|
|
|
signed64 tmp = left;
|
|
|
|
|
|
|
|
// TQString.sprintf("%Ld") did not work :-(, so I had to
|
|
|
|
// do it the old ugly way.
|
|
|
|
while(tmp) {
|
|
|
|
res.insert(0, TQString("%1").arg(static_cast<int>(tmp % 10)));
|
|
|
|
tmp /= 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else
|
|
|
|
res = TQString("%1").arg((long)left);
|
|
|
|
|
|
|
|
if(showThousandSeparator) {
|
|
|
|
int pos = res.length();
|
|
|
|
while((0 < (pos -= 3)) && thousandSeparator())
|
|
|
|
res.insert(pos, thousandSeparator());
|
|
|
|
}
|
|
|
|
|
|
|
|
if(prec > 0 || (prec == -1 && right != 0)) {
|
|
|
|
if(decimalSeparator())
|
|
|
|
res += decimalSeparator();
|
|
|
|
|
|
|
|
// using
|
|
|
|
//
|
|
|
|
// res += TQString("%1").arg(right).rightJustify(prec, '0', true);
|
|
|
|
//
|
|
|
|
// caused some weird results if right was rather large. Eg: right being
|
|
|
|
// 666600000 should have appended a 0, but instead it prepended a 0. With
|
|
|
|
// res being "2," the result wasn't "2,6666000000" as expected, but rather
|
|
|
|
// "2,0666600000" which was not usable. The code below works for me.
|
|
|
|
TQString rs = TQString("%1").arg(right);
|
|
|
|
if(prec != -1)
|
|
|
|
rs = rs.rightJustify(prec, '0', true);
|
|
|
|
else {
|
|
|
|
rs = rs.rightJustify(8, '0', true);
|
|
|
|
// no trailing zeroes or decimal separators
|
|
|
|
while(rs.endsWith("0"))
|
|
|
|
rs.truncate(rs.length()-1);
|
|
|
|
while(rs.endsWith(TQChar(decimalSeparator())))
|
|
|
|
rs.truncate(rs.length()-1);
|
|
|
|
}
|
|
|
|
res += rs;
|
|
|
|
}
|
|
|
|
|
|
|
|
signPosition signpos = bNegative ? _negativeMonetarySignPosition : _positiveMonetarySignPosition;
|
|
|
|
TQString sign = bNegative ? "-" : "";
|
|
|
|
|
|
|
|
switch(signpos) {
|
|
|
|
case ParensAround:
|
|
|
|
res.prepend('(');
|
|
|
|
res.append(')');
|
|
|
|
break;
|
|
|
|
case BeforeQuantityMoney:
|
|
|
|
res.prepend(sign);
|
|
|
|
break;
|
|
|
|
case AfterQuantityMoney:
|
|
|
|
res.append(sign);
|
|
|
|
break;
|
|
|
|
case BeforeMoney:
|
|
|
|
tmpCurrency.prepend(sign);
|
|
|
|
break;
|
|
|
|
case AfterMoney:
|
|
|
|
tmpCurrency.append(sign);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if(!tmpCurrency.isEmpty()) {
|
|
|
|
if(bNegative ? _negativePrefixCurrencySymbol : _positivePrefixCurrencySymbol){
|
|
|
|
res.prepend(' ');
|
|
|
|
res.prepend(tmpCurrency);
|
|
|
|
} else {
|
|
|
|
res.append(' ');
|
|
|
|
res.append(tmpCurrency);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQString MyMoneyMoney::toString(void) const
|
|
|
|
{
|
|
|
|
signed64 tmp = m_num < 0 ? - m_num : m_num;
|
|
|
|
TQString res;
|
|
|
|
TQString resf;
|
|
|
|
|
|
|
|
// TQString.sprintf("%Ld") did not work :-(, so I had to
|
|
|
|
// do it the old ugly way.
|
|
|
|
while(tmp) {
|
|
|
|
res.prepend(TQString("%1").arg(static_cast<int>(tmp % 10)));
|
|
|
|
tmp /= 10;
|
|
|
|
}
|
|
|
|
if(res.isEmpty())
|
|
|
|
res = TQString("0");
|
|
|
|
|
|
|
|
if(m_num < 0)
|
|
|
|
res.prepend('-');
|
|
|
|
|
|
|
|
tmp = m_denom;
|
|
|
|
while(tmp) {
|
|
|
|
resf.prepend(TQString("%1").arg(static_cast<int>(tmp % 10)));
|
|
|
|
tmp /= 10;
|
|
|
|
}
|
|
|
|
return res + "/" + resf;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQDataStream &operator<<(TQDataStream &s, const MyMoneyMoney &_money)
|
|
|
|
{
|
|
|
|
// We WILL lose data here if the user has more than 2 billion pounds :-(
|
|
|
|
// QT defined it here as long:
|
|
|
|
// qglobal.h:typedef long TQ_INT64;
|
|
|
|
|
|
|
|
MyMoneyMoney money = _money.convert(100);
|
|
|
|
|
|
|
|
switch(MyMoneyMoney::_fileVersion) {
|
|
|
|
case MyMoneyMoney::FILE_4_BYTE_VALUE:
|
|
|
|
if(money.m_num & 0xffffffff00000000LL)
|
|
|
|
tqWarning("Lost data while writing out MyMoneyMoney object using deprecated 4 byte writer");
|
|
|
|
|
|
|
|
s << static_cast<TQ_INT32> (money.m_num & 0xffffffff);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
tqDebug("Unknown file version while writing MyMoneyMoney object! Use FILE_8_BYTE_VALUE");
|
|
|
|
// tricky fall through here
|
|
|
|
|
|
|
|
case MyMoneyMoney::FILE_8_BYTE_VALUE:
|
|
|
|
s << static_cast<TQ_INT32> (money.m_num >> 32);
|
|
|
|
s << static_cast<TQ_INT32> (money.m_num & 0xffffffff);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQDataStream &operator>>(TQDataStream &s, MyMoneyMoney &money)
|
|
|
|
{
|
|
|
|
TQ_INT32 tmp;
|
|
|
|
switch(MyMoneyMoney::_fileVersion) {
|
|
|
|
case MyMoneyMoney::FILE_4_BYTE_VALUE:
|
|
|
|
s >> tmp;
|
|
|
|
money.m_num = static_cast<signed64> (tmp);
|
|
|
|
money.m_denom = 100;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
tqDebug("Unknown file version while writing MyMoneyMoney object! FILE_8_BYTE_VALUE assumed");
|
|
|
|
// tricky fall through here
|
|
|
|
|
|
|
|
case MyMoneyMoney::FILE_8_BYTE_VALUE:
|
|
|
|
s >> tmp;
|
|
|
|
money.m_num = static_cast<signed64> (tmp);
|
|
|
|
money.m_num <<= 32;
|
|
|
|
s >> tmp;
|
|
|
|
money.m_num |= static_cast<signed64> (tmp);
|
|
|
|
money.m_denom = 100;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Name: operator+
|
|
|
|
// Purpose: Addition operator - adds the input amount to the object
|
|
|
|
// Returns: The current object
|
|
|
|
// Throws: Nothing.
|
|
|
|
// Arguments: b - MyMoneyMoney object to be added
|
|
|
|
//
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
MyMoneyMoney MyMoneyMoney::operator+( const MyMoneyMoney& _b) const
|
|
|
|
{
|
|
|
|
MyMoneyMoney a(*this);
|
|
|
|
MyMoneyMoney b(_b);
|
|
|
|
MyMoneyMoney sum;
|
|
|
|
signed64 lcd;
|
|
|
|
|
|
|
|
if(a.m_denom < 0) {
|
|
|
|
a.m_num *= a.m_denom;
|
|
|
|
a.m_denom = 1;
|
|
|
|
}
|
|
|
|
if(b.m_denom < 0) {
|
|
|
|
b.m_num *= b.m_denom;
|
|
|
|
b.m_denom = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(a.m_denom == b.m_denom) {
|
|
|
|
sum.m_num = a.m_num + b.m_num;
|
|
|
|
sum.m_denom = a.m_denom;
|
|
|
|
} else {
|
|
|
|
lcd = a.getLcd(b);
|
|
|
|
sum.m_num = a.m_num*(lcd/a.m_denom) + b.m_num*(lcd/b.m_denom);
|
|
|
|
sum.m_denom = lcd;
|
|
|
|
}
|
|
|
|
return sum;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Name: operator-
|
|
|
|
// Purpose: Addition operator - subtracts the input amount from the object
|
|
|
|
// Returns: The current object
|
|
|
|
// Throws: Nothing.
|
|
|
|
// Arguments: AmountInPence - MyMoneyMoney object to be subtracted
|
|
|
|
//
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
MyMoneyMoney MyMoneyMoney::operator-( const MyMoneyMoney& _b) const
|
|
|
|
{
|
|
|
|
MyMoneyMoney a(*this);
|
|
|
|
MyMoneyMoney b(_b);
|
|
|
|
MyMoneyMoney diff;
|
|
|
|
signed64 lcd;
|
|
|
|
|
|
|
|
if(a.m_denom < 0) {
|
|
|
|
a.m_num *= a.m_denom;
|
|
|
|
a.m_denom = 1;
|
|
|
|
}
|
|
|
|
if(b.m_denom < 0) {
|
|
|
|
b.m_num *= b.m_denom;
|
|
|
|
b.m_denom = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(a.m_denom == b.m_denom) {
|
|
|
|
diff.m_num = a.m_num - b.m_num;
|
|
|
|
diff.m_denom = a.m_denom;
|
|
|
|
} else {
|
|
|
|
lcd = a.getLcd(b);
|
|
|
|
diff.m_num = a.m_num*(lcd/a.m_denom) - b.m_num*(lcd/b.m_denom);
|
|
|
|
diff.m_denom = lcd;
|
|
|
|
}
|
|
|
|
return diff;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Name: operator*
|
|
|
|
// Purpose: Multiplication operator - multiplies the input amount to the object
|
|
|
|
// Returns: The current object
|
|
|
|
// Throws: Nothing.
|
|
|
|
// Arguments: b - MyMoneyMoney object to be multiplied
|
|
|
|
//
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
MyMoneyMoney MyMoneyMoney::operator*( const MyMoneyMoney& _b ) const
|
|
|
|
{
|
|
|
|
MyMoneyMoney a(*this);
|
|
|
|
MyMoneyMoney b(_b);
|
|
|
|
MyMoneyMoney product;
|
|
|
|
|
|
|
|
if(a.m_denom < 0) {
|
|
|
|
a.m_num *= a.m_denom;
|
|
|
|
a.m_denom = 1;
|
|
|
|
}
|
|
|
|
if(b.m_denom < 0) {
|
|
|
|
b.m_num *= b.m_denom;
|
|
|
|
b.m_denom = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
product.m_num = a.m_num * b.m_num;
|
|
|
|
product.m_denom = a.m_denom * b.m_denom;
|
|
|
|
|
|
|
|
if(product.m_denom < 0) {
|
|
|
|
product.m_num = -product.m_num;
|
|
|
|
product.m_denom = -product.m_denom;
|
|
|
|
}
|
|
|
|
return product;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Name: operator/
|
|
|
|
// Purpose: Division operator - divides the object by the input amount
|
|
|
|
// Returns: The current object
|
|
|
|
// Throws: Nothing.
|
|
|
|
// Arguments: b - MyMoneyMoney object to be used as dividend
|
|
|
|
//
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
MyMoneyMoney MyMoneyMoney::operator / ( const MyMoneyMoney& _b ) const
|
|
|
|
{
|
|
|
|
MyMoneyMoney a(*this);
|
|
|
|
MyMoneyMoney b(_b);
|
|
|
|
MyMoneyMoney quotient;
|
|
|
|
signed64 lcd;
|
|
|
|
|
|
|
|
if(a.m_denom < 0) {
|
|
|
|
a.m_num *= a.m_denom;
|
|
|
|
a.m_denom = 1;
|
|
|
|
}
|
|
|
|
if(b.m_denom < 0) {
|
|
|
|
b.m_num *= b.m_denom;
|
|
|
|
b.m_denom = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(a.m_denom == b.m_denom) {
|
|
|
|
quotient.m_num = a.m_num;
|
|
|
|
quotient.m_denom = b.m_num;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* ok, convert to the lcd and compute from there... */
|
|
|
|
lcd = a.getLcd(b);
|
|
|
|
quotient.m_num = a.m_num*(lcd/a.m_denom);
|
|
|
|
quotient.m_denom = b.m_num*(lcd/b.m_denom);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(quotient.m_denom < 0) {
|
|
|
|
quotient.m_num = -quotient.m_num;
|
|
|
|
quotient.m_denom = -quotient.m_denom;
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_ASSERT(quotient.m_denom != 0);
|
|
|
|
|
|
|
|
return quotient;
|
|
|
|
}
|
|
|
|
|
|
|
|
signed64 MyMoneyMoney::getLcd(const MyMoneyMoney& b) const
|
|
|
|
{
|
|
|
|
signed64 current_divisor = 2;
|
|
|
|
signed64 max_square;
|
|
|
|
signed64 three_count = 0;
|
|
|
|
signed64 small_denom;
|
|
|
|
signed64 big_denom;
|
|
|
|
|
|
|
|
if(b.m_denom < m_denom) {
|
|
|
|
small_denom = b.m_denom;
|
|
|
|
big_denom = m_denom;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
small_denom = m_denom;
|
|
|
|
big_denom = b.m_denom;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* special case: smaller divides smoothly into larger */
|
|
|
|
if((big_denom % small_denom) == 0) {
|
|
|
|
return big_denom;
|
|
|
|
}
|
|
|
|
|
|
|
|
max_square = small_denom;
|
|
|
|
|
|
|
|
/* the LCM algorithm : factor out the union of the prime factors of the
|
|
|
|
* two args and then multiply the remainders together.
|
|
|
|
*
|
|
|
|
* To do this, we find the successive prime factors of the smaller
|
|
|
|
* denominator and eliminate them from both the smaller and larger
|
|
|
|
* denominator (so we only count factors on a one-on-one basis),
|
|
|
|
* then multiply the original smaller by the remains of the larger.
|
|
|
|
*
|
|
|
|
* I.e. LCM 100,96875 == 2*2*5*5,31*5*5*5*5 = 2*2,31*5*5
|
|
|
|
* answer: multiply 100 by 31*5*5 == 387500
|
|
|
|
*/
|
|
|
|
while((current_divisor * current_divisor) <= max_square) {
|
|
|
|
if(((small_denom % current_divisor) == 0) &&
|
|
|
|
((big_denom % current_divisor) == 0)) {
|
|
|
|
big_denom = big_denom / current_divisor;
|
|
|
|
small_denom = small_denom / current_divisor;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if(current_divisor == 2) {
|
|
|
|
current_divisor++;
|
|
|
|
}
|
|
|
|
else if(three_count == 3) {
|
|
|
|
current_divisor += 4;
|
|
|
|
three_count = 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
current_divisor += 2;
|
|
|
|
three_count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if((current_divisor > small_denom) ||
|
|
|
|
(current_divisor > big_denom)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* max_sqaure is the original small_denom */
|
|
|
|
return max_square * big_denom;
|
|
|
|
}
|
|
|
|
|
|
|
|
const MyMoneyMoney MyMoneyMoney::convert(const signed64 _denom, const roundingMethod how) const
|
|
|
|
{
|
|
|
|
MyMoneyMoney out(*this);
|
|
|
|
MyMoneyMoney in (*this);
|
|
|
|
MyMoneyMoney temp;
|
|
|
|
|
|
|
|
signed64 denom = _denom;
|
|
|
|
signed64 temp_bc;
|
|
|
|
signed64 temp_a;
|
|
|
|
signed64 remainder;
|
|
|
|
signed64 sign;
|
|
|
|
int denom_neg=0;
|
|
|
|
|
|
|
|
if(m_denom != denom) {
|
|
|
|
/* if the denominator of the input value is negative, get rid of that. */
|
|
|
|
if(m_denom < 0) {
|
|
|
|
in.m_num = in.m_num * (- in.m_denom);
|
|
|
|
in.m_denom = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
sign = (in.m_num < 0) ? -1 : 1;
|
|
|
|
|
|
|
|
/* if the denominator is less than zero, we are to interpret it as
|
|
|
|
* the reciprocal of its magnitude. */
|
|
|
|
if(denom < 0) {
|
|
|
|
denom = - denom;
|
|
|
|
denom_neg = 1;
|
|
|
|
temp_a = (in.m_num < 0) ? -in.m_num : in.m_num;
|
|
|
|
temp_bc = in.m_denom * denom;
|
|
|
|
remainder = in.m_num % temp_bc;
|
|
|
|
out.m_num = in.m_num / temp_bc;
|
|
|
|
out.m_denom = -denom;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* do all the modulo and int division on positive values to make
|
|
|
|
* things a little clearer. Reduce the fraction denom/in.denom to
|
|
|
|
* help with range errors (FIXME : need bigger intermediate rep) */
|
|
|
|
temp.m_num = denom;
|
|
|
|
temp.m_denom = in.m_denom;
|
|
|
|
temp = temp.reduce();
|
|
|
|
|
|
|
|
out.m_num = in.m_num * temp.m_num;
|
|
|
|
out.m_num = (out.m_num < 0) ? -out.m_num : out.m_num;
|
|
|
|
remainder = out.m_num % temp.m_denom;
|
|
|
|
out.m_num = out.m_num / temp.m_denom;
|
|
|
|
out.m_denom = denom;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(remainder > 0) {
|
|
|
|
switch(how) {
|
|
|
|
case RndFloor:
|
|
|
|
if(sign < 0) {
|
|
|
|
out.m_num = out.m_num + 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RndCeil:
|
|
|
|
if(sign > 0) {
|
|
|
|
out.m_num = out.m_num + 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RndTrunc:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RndPromote:
|
|
|
|
out.m_num = out.m_num + 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RndHalfDown:
|
|
|
|
if(denom_neg) {
|
|
|
|
if((2 * remainder) > in.m_denom*denom) {
|
|
|
|
out.m_num = out.m_num + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if((2 * remainder) > temp.m_denom) {
|
|
|
|
out.m_num = out.m_num + 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RndHalfUp:
|
|
|
|
if(denom_neg) {
|
|
|
|
if((2 * remainder) >= in.m_denom*denom) {
|
|
|
|
out.m_num = out.m_num + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if((2 * remainder ) >= temp.m_denom) {
|
|
|
|
out.m_num = out.m_num + 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RndRound:
|
|
|
|
if(denom_neg) {
|
|
|
|
if((2 * remainder) > in.m_denom*denom) {
|
|
|
|
out.m_num = out.m_num + 1;
|
|
|
|
}
|
|
|
|
else if((2 * remainder) == in.m_denom*denom) {
|
|
|
|
if(out.m_num % 2) {
|
|
|
|
out.m_num = out.m_num + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if((2 * remainder ) > temp.m_denom) {
|
|
|
|
out.m_num = out.m_num + 1;
|
|
|
|
}
|
|
|
|
else if((2 * remainder) == temp.m_denom) {
|
|
|
|
if(out.m_num % 2) {
|
|
|
|
out.m_num = out.m_num + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RndNever:
|
|
|
|
tqWarning("MyMoneyMoney: have remainder \"%Ld/%Ld\"->convert(%Ld, %d)",
|
|
|
|
m_num, m_denom, _denom, how);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
out.m_num = (sign > 0) ? out.m_num : (-out.m_num);
|
|
|
|
}
|
|
|
|
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/********************************************************************
|
|
|
|
* gnc_numeric_reduce
|
|
|
|
* reduce a fraction by GCF elimination. This is NOT done as a
|
|
|
|
* part of the arithmetic API unless GNC_DENOM_REDUCE is specified
|
|
|
|
* as the output denominator.
|
|
|
|
********************************************************************/
|
|
|
|
const MyMoneyMoney MyMoneyMoney::reduce(void) const
|
|
|
|
{
|
|
|
|
MyMoneyMoney out;
|
|
|
|
signed64 t;
|
|
|
|
signed64 num = (m_num < 0) ? (- m_num) : m_num ;
|
|
|
|
signed64 denom = m_denom;
|
|
|
|
|
|
|
|
/* the strategy is to use euclid's algorithm */
|
|
|
|
while (denom > 0) {
|
|
|
|
t = num % denom;
|
|
|
|
num = denom;
|
|
|
|
denom = t;
|
|
|
|
}
|
|
|
|
/* num = gcd */
|
|
|
|
|
|
|
|
/* all calculations are done on positive num, since it's not
|
|
|
|
* well defined what % does for negative values */
|
|
|
|
out.m_num = m_num / num;
|
|
|
|
out.m_denom = m_denom / num;
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
signed64 MyMoneyMoney::precToDenom(int prec)
|
|
|
|
{
|
|
|
|
signed64 denom = 1;
|
|
|
|
|
|
|
|
while(prec--)
|
|
|
|
denom *= 10;
|
|
|
|
|
|
|
|
return denom;
|
|
|
|
}
|
|
|
|
|
|
|
|
double MyMoneyMoney::toDouble(void) const
|
|
|
|
{
|
|
|
|
return static_cast<double>(m_num) / static_cast<double>(m_denom);
|
|
|
|
}
|
|
|
|
|
|
|
|
int MyMoneyMoney::denomToPrec(signed64 fract)
|
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
while(fract > 1) {
|
|
|
|
rc++;
|
|
|
|
fract /= 10;
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
MyMoneyMoney::operator int() const
|
|
|
|
{
|
|
|
|
return static_cast<int> (m_num / m_denom);
|
|
|
|
}
|