File indexing completed on 2024-05-12 16:42:38
0001 /* 0002 SPDX-FileCopyrightText: 2000-2004 Michael Edwardes <mte@users.sourceforge.net> 0003 SPDX-FileCopyrightText: 2001-2021 Thomas Baumgart <tbaumgart@kde.org> 0004 SPDX-FileCopyrightText: 2001-2002 Felix Rodriguez <frodriguez@users.sourceforge.net> 0005 SPDX-FileCopyrightText: 2002-2004 Kevin Tambascio <ktambascio@users.sourceforge.net> 0006 SPDX-FileCopyrightText: 2005 Ace Jones <acejones@users.sourceforge.net> 0007 SPDX-FileCopyrightText: 2007-2010 Alvaro Soliverez <asoliverez@gmail.com> 0008 SPDX-FileCopyrightText: 2011 Carlos Eduardo da Silva <kaduardo@gmail.com> 0009 SPDX-FileCopyrightText: 2017-2018 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com> 0010 SPDX-License-Identifier: GPL-2.0-or-later 0011 */ 0012 0013 // make sure, that this is defined before we even include any other header file 0014 #ifndef __STDC_LIMIT_MACROS 0015 #define __STDC_LIMIT_MACROS // force definition of min and max values 0016 #endif 0017 0018 #include "mymoneymoney.h" 0019 0020 #include <stdint.h> 0021 #include <gmpxx.h> 0022 0023 // ---------------------------------------------------------------------------- 0024 // QT Includes 0025 0026 #include <QDebug> 0027 #include <QString> 0028 0029 // ---------------------------------------------------------------------------- 0030 // Project Includes 0031 0032 #include "mymoneyexception.h" 0033 #include "mymoneyenums.h" 0034 0035 const MyMoneyMoney MyMoneyMoney::ONE = MyMoneyMoney(1, 1); 0036 const MyMoneyMoney MyMoneyMoney::MINUS_ONE = MyMoneyMoney(-1, 1); 0037 namespace eMyMoney 0038 { 0039 namespace Money { 0040 0041 enum fileVersionE : int { 0042 FILE_4_BYTE_VALUE = 0, 0043 FILE_8_BYTE_VALUE, 0044 }; 0045 0046 QChar _thousandSeparator = QLatin1Char(','); 0047 QChar _decimalSeparator = QLatin1Char('.'); 0048 eMyMoney::Money::signPosition _negativeMonetarySignPosition = PreceedQuantityAndSymbol; 0049 eMyMoney::Money::signPosition _positiveMonetarySignPosition = PreceedQuantityAndSymbol; 0050 bool _negativePrefixCurrencySymbol = false; 0051 bool _positivePrefixCurrencySymbol = false; 0052 bool _negativeSpaceSeparatesSymbol = true; 0053 bool _positiveSpaceSeparatesSymbol = true; 0054 eMyMoney::Money::fileVersionE _fileVersion = fileVersionE::FILE_4_BYTE_VALUE; 0055 } 0056 } 0057 0058 MyMoneyMoney MyMoneyMoney::maxValue = MyMoneyMoney(INT64_MAX, 100); 0059 MyMoneyMoney MyMoneyMoney::minValue = MyMoneyMoney(INT64_MIN, 100); 0060 MyMoneyMoney MyMoneyMoney::autoCalc = MyMoneyMoney(INT64_MIN + 1, 100); 0061 0062 void MyMoneyMoney::setNegativeSpaceSeparatesSymbol(const bool flag) 0063 { 0064 eMyMoney::Money::_negativeSpaceSeparatesSymbol= flag; 0065 } 0066 0067 void MyMoneyMoney::setPositiveSpaceSeparatesSymbol(const bool flag) 0068 { 0069 eMyMoney::Money::_positiveSpaceSeparatesSymbol= flag; 0070 } 0071 0072 void MyMoneyMoney::setNegativePrefixCurrencySymbol(const bool flag) 0073 { 0074 eMyMoney::Money::_negativePrefixCurrencySymbol = flag; 0075 } 0076 0077 void MyMoneyMoney::setPositivePrefixCurrencySymbol(const bool flag) 0078 { 0079 eMyMoney::Money::_positivePrefixCurrencySymbol = flag; 0080 } 0081 0082 void MyMoneyMoney::setNegativeMonetarySignPosition(const eMyMoney::Money::signPosition pos) 0083 { 0084 eMyMoney::Money::_negativeMonetarySignPosition = pos; 0085 } 0086 0087 eMyMoney::Money::signPosition MyMoneyMoney::negativeMonetarySignPosition() 0088 { 0089 return eMyMoney::Money::_negativeMonetarySignPosition; 0090 } 0091 0092 void MyMoneyMoney::setPositiveMonetarySignPosition(const eMyMoney::Money::signPosition pos) 0093 { 0094 eMyMoney::Money::_positiveMonetarySignPosition = pos; 0095 } 0096 0097 eMyMoney::Money::signPosition MyMoneyMoney::positiveMonetarySignPosition() 0098 { 0099 return eMyMoney::Money::_positiveMonetarySignPosition; 0100 } 0101 0102 void MyMoneyMoney::setThousandSeparator(const QChar &separator) 0103 { 0104 if (separator != QLatin1Char(' ')) 0105 eMyMoney::Money::_thousandSeparator = separator; 0106 else 0107 eMyMoney::Money::_thousandSeparator = 0; 0108 } 0109 0110 const QChar MyMoneyMoney::thousandSeparator() 0111 { 0112 return eMyMoney::Money::_thousandSeparator; 0113 } 0114 0115 void MyMoneyMoney::setDecimalSeparator(const QChar &separator) 0116 { 0117 if (separator != QLatin1Char(' ')) 0118 eMyMoney::Money::_decimalSeparator = separator; 0119 else 0120 eMyMoney::Money::_decimalSeparator = 0; 0121 } 0122 0123 const QChar MyMoneyMoney::decimalSeparator() 0124 { 0125 return eMyMoney::Money::_decimalSeparator; 0126 } 0127 0128 MyMoneyMoney::MyMoneyMoney(const QString& pszAmount) 0129 : AlkValue(pszAmount, eMyMoney::Money::_decimalSeparator) 0130 { 0131 } 0132 0133 //////////////////////////////////////////////////////////////////////////////// 0134 // Name: MyMoneyMoney 0135 // Purpose: Constructor - constructs object from an amount in a signed64 value 0136 // Returns: None 0137 // Throws: Nothing. 0138 // Arguments: Amount - signed 64 object containing amount 0139 // denom - denominator of the object 0140 // 0141 //////////////////////////////////////////////////////////////////////////////// 0142 MyMoneyMoney::MyMoneyMoney(qint64 Amount, const unsigned int denom) 0143 { 0144 if (denom == 0) 0145 throw MYMONEYEXCEPTION_CSTRING("Denominator 0 not allowed!"); 0146 0147 *this = AlkValue(QString::fromLatin1("%1/%2").arg(Amount).arg(denom), eMyMoney::Money::_decimalSeparator); 0148 } 0149 0150 //////////////////////////////////////////////////////////////////////////////// 0151 // Name: MyMoneyMoney 0152 // Purpose: Constructor - constructs object from an amount in a integer value 0153 // Returns: None 0154 // Throws: Nothing. 0155 // Arguments: iAmount - integer object containing amount 0156 // denom - denominator of the object 0157 // 0158 //////////////////////////////////////////////////////////////////////////////// 0159 MyMoneyMoney::MyMoneyMoney(const int iAmount, const unsigned int denom) 0160 { 0161 if (denom == 0) 0162 throw MYMONEYEXCEPTION_CSTRING("Denominator 0 not allowed!"); 0163 *this = AlkValue(iAmount, denom); 0164 } 0165 0166 //////////////////////////////////////////////////////////////////////////////// 0167 // Name: MyMoneyMoney 0168 // Purpose: Constructor - constructs object from an amount in a long integer value 0169 // Returns: None 0170 // Throws: Nothing. 0171 // Arguments: iAmount - integer object containing amount 0172 // denom - denominator of the object 0173 // 0174 //////////////////////////////////////////////////////////////////////////////// 0175 MyMoneyMoney::MyMoneyMoney(const long int iAmount, const unsigned int denom) 0176 { 0177 if (denom == 0) 0178 throw MYMONEYEXCEPTION_CSTRING("Denominator 0 not allowed!"); 0179 *this = AlkValue(QString::fromLatin1("%1/%2").arg(iAmount).arg(denom), eMyMoney::Money::_decimalSeparator); 0180 } 0181 0182 0183 MyMoneyMoney::~MyMoneyMoney() 0184 { 0185 } 0186 0187 MyMoneyMoney MyMoneyMoney::abs() const 0188 { 0189 return static_cast<const MyMoneyMoney>(AlkValue::abs()); 0190 } 0191 0192 QString MyMoneyMoney::formatMoney(int denom, bool showThousandSeparator) const 0193 { 0194 return formatMoney(QString(), denomToPrec(denom), showThousandSeparator); 0195 } 0196 0197 QString MyMoneyMoney::formatMoney(const QString& currency, const int prec, bool showThousandSeparator) const 0198 { 0199 QString res; 0200 QString tmpCurrency = currency; 0201 int tmpPrec = prec; 0202 mpz_class denom = 1; 0203 mpz_class value; 0204 const int maxPrecision = 20; 0205 0206 // if prec == -1 we want the maximum possible but w/o trailing zeroes 0207 if (tmpPrec == -1) { 0208 tmpPrec = maxPrecision; 0209 } 0210 mpz_ui_pow_ui(denom.get_mpz_t(), 10, tmpPrec); 0211 0212 value = static_cast<const MyMoneyMoney>(convertDenominator(denom)).valueRef().get_num(); 0213 0214 bool bNegative = false; 0215 mpz_class left = value / static_cast<MyMoneyMoney>(convertDenominator(denom)).valueRef().get_den(); 0216 mpz_class right = mpz_class((valueRef() - mpq_class(left)) * denom); 0217 0218 if (right < 0) { 0219 right = -right; 0220 bNegative = true; 0221 } 0222 if (left < 0) { 0223 left = -left; 0224 bNegative = true; 0225 } 0226 0227 // convert the integer (left) part to a string 0228 res.append(left.get_str().c_str()); 0229 0230 // if requested, insert thousand separators every three digits 0231 if (showThousandSeparator) { 0232 int pos = res.length(); 0233 while ((0 < (pos -= 3)) && thousandSeparator() != 0) 0234 res.insert(pos, thousandSeparator()); 0235 } 0236 0237 // take care of the fractional part 0238 if (prec > 0 || (prec == -1 && right != 0)) { 0239 if (decimalSeparator() != 0) 0240 res += decimalSeparator(); 0241 0242 auto rs = QString::fromLatin1("%1").arg(right.get_str().c_str()); 0243 if (prec != -1) 0244 rs = rs.rightJustified(prec, QLatin1Char('0'), true); 0245 else { 0246 rs = rs.rightJustified(maxPrecision, QLatin1Char('0'), true); 0247 // no trailing zeroes or decimal separators 0248 while (rs.endsWith(QLatin1Char('0'))) 0249 rs.truncate(rs.length() - 1); 0250 while (rs.endsWith(decimalSeparator())) 0251 rs.truncate(rs.length() - 1); 0252 } 0253 res += rs; 0254 } 0255 0256 eMyMoney::Money::signPosition signpos = bNegative ? eMyMoney::Money::_negativeMonetarySignPosition : eMyMoney::Money::_positiveMonetarySignPosition; 0257 auto sign = bNegative ? QString::fromLatin1("-") : QString(); 0258 0259 switch (signpos) { 0260 case eMyMoney::Money::ParensAround: 0261 // do nothing here 0262 break; 0263 case eMyMoney::Money::PreceedQuantityAndSymbol: 0264 res.prepend(sign); 0265 break; 0266 case eMyMoney::Money::SucceedQuantityAndSymbol: 0267 res.append(sign); 0268 break; 0269 case eMyMoney::Money::PreceedSymbol: 0270 tmpCurrency.prepend(sign); 0271 break; 0272 case eMyMoney::Money::SucceedSymbol: 0273 tmpCurrency.append(sign); 0274 break; 0275 } 0276 if (!tmpCurrency.isEmpty()) { 0277 if (bNegative ? eMyMoney::Money::_negativePrefixCurrencySymbol : eMyMoney::Money::_positivePrefixCurrencySymbol) { 0278 if (bNegative ? eMyMoney::Money::_negativeSpaceSeparatesSymbol : eMyMoney::Money::_positiveSpaceSeparatesSymbol) { 0279 res.prepend(QLatin1Char(' ')); 0280 } 0281 res.prepend(tmpCurrency); 0282 } else { 0283 if (bNegative ? eMyMoney::Money::_negativeSpaceSeparatesSymbol : eMyMoney::Money::_positiveSpaceSeparatesSymbol) { 0284 res.append(QLatin1Char(' ')); 0285 } 0286 res.append(tmpCurrency); 0287 } 0288 } 0289 0290 if (signpos == eMyMoney::Money::ParensAround) { 0291 res.prepend(QLatin1Char('(')); 0292 res.append(QLatin1Char(')')); 0293 } 0294 0295 return res; 0296 } 0297 0298 //////////////////////////////////////////////////////////////////////////////// 0299 // Name: operator+ 0300 // Purpose: Addition operator - adds the input amount to the object 0301 // Returns: The current object 0302 // Throws: Nothing. 0303 // Arguments: b - MyMoneyMoney object to be added 0304 // 0305 //////////////////////////////////////////////////////////////////////////////// 0306 const MyMoneyMoney MyMoneyMoney::operator+(const MyMoneyMoney& _b) const 0307 { 0308 return static_cast<const MyMoneyMoney>(AlkValue::operator+(_b)); 0309 } 0310 0311 0312 //////////////////////////////////////////////////////////////////////////////// 0313 // Name: operator- 0314 // Purpose: Addition operator - subtracts the input amount from the object 0315 // Returns: The current object 0316 // Throws: Nothing. 0317 // Arguments: AmountInPence - MyMoneyMoney object to be subtracted 0318 // 0319 //////////////////////////////////////////////////////////////////////////////// 0320 const MyMoneyMoney MyMoneyMoney::operator-(const MyMoneyMoney& _b) const 0321 { 0322 return static_cast<const MyMoneyMoney>(AlkValue::operator-(_b)); 0323 } 0324 0325 //////////////////////////////////////////////////////////////////////////////// 0326 // Name: operator* 0327 // Purpose: Multiplication operator - multiplies the input amount to the object 0328 // Returns: The current object 0329 // Throws: Nothing. 0330 // Arguments: b - MyMoneyMoney object to be multiplied 0331 // 0332 //////////////////////////////////////////////////////////////////////////////// 0333 const MyMoneyMoney MyMoneyMoney::operator*(const MyMoneyMoney& _b) const 0334 { 0335 return static_cast<const MyMoneyMoney>(AlkValue::operator*(_b)); 0336 } 0337 0338 //////////////////////////////////////////////////////////////////////////////// 0339 // Name: operator/ 0340 // Purpose: Division operator - divides the object by the input amount 0341 // Returns: The current object 0342 // Throws: Nothing. 0343 // Arguments: b - MyMoneyMoney object to be used as dividend 0344 // 0345 //////////////////////////////////////////////////////////////////////////////// 0346 const MyMoneyMoney MyMoneyMoney::operator/(const MyMoneyMoney& _b) const 0347 { 0348 return static_cast<const MyMoneyMoney>(AlkValue::operator/(_b)); 0349 } 0350 0351 bool MyMoneyMoney::isNegative() const 0352 { 0353 return (valueRef() < 0) ? true : false; 0354 } 0355 0356 bool MyMoneyMoney::isPositive() const 0357 { 0358 return (valueRef() > 0) ? true : false; 0359 } 0360 0361 bool MyMoneyMoney::isZero() const 0362 { 0363 return valueRef() == 0; 0364 } 0365 0366 bool MyMoneyMoney::isAutoCalc() const 0367 { 0368 return (*this == autoCalc); 0369 } 0370 0371 MyMoneyMoney MyMoneyMoney::convert(const signed64 _denom, const AlkValue::RoundingMethod how) const 0372 { 0373 return static_cast<const MyMoneyMoney>(AlkValue::convertDenominator(_denom, how)); 0374 } 0375 0376 MyMoneyMoney MyMoneyMoney::reduce() const 0377 { 0378 MyMoneyMoney out(*this); 0379 out.canonicalize(); 0380 return out; 0381 } 0382 0383 signed64 MyMoneyMoney::precToDenom(int prec) 0384 { 0385 signed64 denom = 1; 0386 0387 while (prec--) 0388 denom *= 10; 0389 0390 return denom; 0391 } 0392 0393 double MyMoneyMoney::toDouble() const 0394 { 0395 return valueRef().get_d(); 0396 } 0397 0398 int MyMoneyMoney::denomToPrec(signed64 fract) 0399 { 0400 int rc = 0; 0401 while (fract > 1) { 0402 rc++; 0403 fract /= 10; 0404 } 0405 return rc; 0406 } 0407 0408 /** 0409 * @todo Eventually move this to AlkValue 0410 */ 0411 MyMoneyMoney MyMoneyMoney::convertDenominator(mpz_class denom, const AlkValue::RoundingMethod how) const 0412 { 0413 MyMoneyMoney in(*this); 0414 mpz_class in_num(mpq_numref(in.valueRef().get_mpq_t())); 0415 0416 MyMoneyMoney out; // initialize to zero 0417 0418 int sign = sgn(in_num); 0419 if (sign != 0) { 0420 // sign is either -1 for negative numbers or +1 in all other cases 0421 0422 MyMoneyMoney temp; 0423 // only process in case the denominators are different 0424 if (mpz_cmpabs(denom.get_mpz_t(), mpq_denref(valueRef().get_mpq_t())) != 0) { 0425 mpz_class in_denom(mpq_denref(in.valueRef().get_mpq_t())); 0426 mpz_class out_num, out_denom; 0427 0428 if (sgn(in_denom) == -1) { // my denom is negative 0429 in_num = in_num * (-in_denom); 0430 in_num = 1; 0431 } 0432 0433 mpz_class remainder; 0434 int denom_neg = 0; 0435 0436 // if the denominator is less than zero, we are to interpret it as 0437 // the reciprocal of its magnitude. 0438 if (sgn(denom) < 0) { 0439 mpz_class temp_a; 0440 mpz_class temp_bc; 0441 denom = -denom; 0442 denom_neg = 1; 0443 temp_a = ::abs(in_num); 0444 temp_bc = in_denom * denom; 0445 remainder = temp_a % temp_bc; 0446 out_num = temp_a / temp_bc; 0447 out_denom = denom; 0448 } else { 0449 temp = AlkValue(denom, in_denom); 0450 // the canonicalization required here is part of the ctor 0451 // temp.d->m_val.canonicalize(); 0452 0453 out_num = ::abs(in_num * temp.valueRef().get_num()); 0454 remainder = out_num % temp.valueRef().get_den(); 0455 out_num = out_num / temp.valueRef().get_den(); 0456 out_denom = denom; 0457 } 0458 0459 if (remainder != 0) { 0460 switch (how) { 0461 case RoundFloor: 0462 if (sign < 0) { 0463 out_num = out_num + 1; 0464 } 0465 break; 0466 0467 case RoundCeil: 0468 if (sign > 0) { 0469 out_num = out_num + 1; 0470 } 0471 break; 0472 0473 case RoundTruncate: 0474 break; 0475 0476 case RoundPromote: 0477 out_num = out_num + 1; 0478 break; 0479 0480 case RoundHalfDown: 0481 if (denom_neg) { 0482 if ((2 * remainder) > (in_denom * denom)) { 0483 out_num = out_num + 1; 0484 } 0485 } else if ((2 * remainder) > (temp.valueRef().get_den())) { 0486 out_num = out_num + 1; 0487 } 0488 break; 0489 0490 case RoundHalfUp: 0491 if (denom_neg) { 0492 if ((2 * remainder) >= (in_denom * denom)) { 0493 out_num = out_num + 1; 0494 } 0495 } else if ((2 * remainder) >= temp.valueRef().get_den()) { 0496 out_num = out_num + 1; 0497 } 0498 break; 0499 0500 case RoundRound: 0501 if (denom_neg) { 0502 if ((remainder * 2) > (in_denom * denom)) { 0503 out_num = out_num + 1; 0504 } else if ((2 * remainder) == (in_denom * denom)) { 0505 if ((out_num % 2) != 0) { 0506 out_num = out_num + 1; 0507 } 0508 } 0509 } else { 0510 if ((remainder * 2) > temp.valueRef().get_den()) { 0511 out_num = out_num + 1; 0512 } else if ((2 * remainder) == temp.valueRef().get_den()) { 0513 if ((out_num % 2) != 0) { 0514 out_num = out_num + 1; 0515 } 0516 } 0517 } 0518 break; 0519 0520 case RoundNever: 0521 qWarning() << "AlkValue: have remainder" << toString() << "->convertDenominator()"; 0522 break; 0523 } 0524 } 0525 0526 // construct the new output value 0527 out = AlkValue(out_num * sign, out_denom); 0528 } else { 0529 out = *this; 0530 } 0531 } 0532 return out; 0533 }