Mercurial > hgweb.cgi > pecunia
comparison src/pecunia/Money.hpp @ 69:e106fadfad66
Selected the final name for the library and updated everything accordingly.
author | John Schneiderman <JohnMS@member.fsf.org> |
---|---|
date | Sun, 17 Jun 2018 08:18:48 +0200 |
parents | src/iso-currency/Money.hpp@b11cc8d9547e |
children | 924a06bf271d |
comparison
equal
deleted
inserted
replaced
68:ceba2d02ec48 | 69:e106fadfad66 |
---|---|
1 /******************************************************************************* | |
2 *** This file is part of Pecunia. *** | |
3 *** *** | |
4 *** Copyright (C) 2016 *** | |
5 *** John Schneiderman <Licensing _AT_ Schneiderman _DOT_ me> *** | |
6 *** *** | |
7 *** This program is free software: you can redistribute it and/or modify it *** | |
8 *** under the terms of the GNU Lesser General Public License as published *** | |
9 *** by the Free Software Foundation, either version 3 of the License, or *** | |
10 *** (at your option) any later version. *** | |
11 *** *** | |
12 *** This program is distributed in the hope that it will be useful, but *** | |
13 *** WITHOUT ANY WARRANTY; without even the implied warranty of *** | |
14 *** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *** | |
15 *** See the GNU Lesser General Public License for more details. *** | |
16 *** *** | |
17 *** You should have received a copy of the GNU Lesser General Public License*** | |
18 *** along with this program. If not, see <http://www.gnu.org/licenses/>. *** | |
19 *******************************************************************************/ | |
20 #ifndef PECUNIA_CURRENCY_MONEY_HPP_ | |
21 #define PECUNIA_CURRENCY_MONEY_HPP_ | |
22 | |
23 #include <cassert> | |
24 #include <cstdint> | |
25 #include <cmath> | |
26 #include <functional> | |
27 #include <iomanip> | |
28 #include <istream> | |
29 #include <limits> | |
30 #include <locale> | |
31 #include <ostream> | |
32 #include <stdexcept> | |
33 #include <string> | |
34 #include <sstream> | |
35 #include <type_traits> | |
36 | |
37 #include "Adjustments.hpp" | |
38 #include "Codes.h" | |
39 #include "MoneyManip.h" | |
40 #include "MoneyTypes.h" | |
41 #include "internal/Verification.hpp" | |
42 | |
43 | |
44 namespace pecunia | |
45 { | |
46 namespace currency | |
47 { | |
48 | |
49 /** | |
50 * A representation of a country's currency supporting basic arithmetic usage and guaranteeing all | |
51 * operations succeed without a loss of precision, overflow, or underflow. This version allows for | |
52 * reading in an arbitrary currency. | |
53 * | |
54 * @tparam precision The number of digits after a minor unit to guarantee are always correct. | |
55 * @tparam MinorUnit The storage type for the minor unit of the monetary value. This type must | |
56 * be an unsigned internal type. | |
57 * @tparam UnitStorage The internal storage container for the monetary and major value. | |
58 * @tparam FloatingPoint The floating-point type to accept and convert into. This type must be | |
59 * a floating-point type. | |
60 */ | |
61 template | |
62 < | |
63 std::uint8_t precision = 2, | |
64 typename MinorUnit = std::uint16_t, | |
65 typename UnitStorage = std::int64_t, | |
66 typename FloatingPoint = double | |
67 > | |
68 class Money | |
69 { | |
70 static_assert(std::is_unsigned<MinorUnit>::value, "The MinorUnit must be unsigned."); | |
71 static_assert(std::is_integral<MinorUnit>::value, "The MinorUnit must be an integer."); | |
72 static_assert( | |
73 std::is_floating_point<FloatingPoint>::value, | |
74 "The FloatingPoint is not a floating-point type." | |
75 ); | |
76 | |
77 /** | |
78 * Calculates the largest storable monetary major value. | |
79 */ | |
80 UnitStorage maximumMajorValue(); | |
81 /** | |
82 * Calculates the smallest storable monetary major value. | |
83 */ | |
84 UnitStorage minimumMajorValue(); | |
85 /** | |
86 * Calculates the largest storable monetary minor value. | |
87 */ | |
88 MinorUnit maximumMinorValue(); | |
89 /** | |
90 * Calculates the largest storable monetary amount value. | |
91 */ | |
92 UnitStorage maximumAmountValue(); | |
93 /** | |
94 * Calculates the smallest storable monetary amount value. | |
95 */ | |
96 UnitStorage minimumAmountValue(); | |
97 | |
98 /// The entire representation of the monetary amount using the currency's minor unit, e.g. cents | |
99 /// for USD, plus any extra digits of precision. | |
100 UnitStorage amount_; | |
101 /// The ISO 4217 currency code the monetary amount represents. | |
102 Codes iso4217Code_; | |
103 | |
104 public: | |
105 /** | |
106 * Constructor for a fully formed object that initialises the stored amount to zero. | |
107 * | |
108 * @param[in] code The ISO 4217 currency code the amount represents. | |
109 */ | |
110 Money(const Codes code = Codes::XXX); | |
111 /** | |
112 * Constructor for setting an exact minor monetary value whose major value is zero. The minor | |
113 * value is expected to be adjusted for the level of precision the object is expecting, e.g. | |
114 * U.S.A. dollars with a precision of two would enter twelve cents as 1200. | |
115 * | |
116 * @pre When the minor value is zero, the sign must be Sign::Neither. | |
117 * | |
118 * @exception overflow_error When the minor unit is too large to be stored. | |
119 * | |
120 * @param[in] minor The amount of minor currency to store, e.g. U.S.A. cents. | |
121 * @param[in] sign The associated signedness of the minor value, e.g. negative. | |
122 * @param[in] code The ISO 4217 currency code the amount represents. | |
123 */ | |
124 Money(const MinorUnit minor, const MinorSign sign, const Codes code = Codes::XXX); | |
125 /** | |
126 * Constructor for setting an exact monetary value of a fully formed object. The minor value is | |
127 * expected to be adjusted for the level of precision the object is expecting, e.g. U.S.A. | |
128 * dollars with a precision of two would enter twelve cents as 1200. | |
129 * | |
130 * @pre The maximum minor value must fit within the MinorUnit. | |
131 * | |
132 * @exception overflow_error When the major unit is too large to be stored in a positive direction. | |
133 * @exception overflow_error When the major unit is too small to be stored in a negative direction. | |
134 * @exception overflow_error When the minor unit is too large to be stored. | |
135 * | |
136 * @param[in] major The amount of major currency to store, e.g. U.S.A. dollars. | |
137 * @param[in] minor The amount of minor currency to store, e.g. U.S.A. cents. | |
138 * @param[in] code The ISO 4217 currency code the amount represents. | |
139 */ | |
140 Money(const UnitStorage major, const MinorUnit minor, const Codes code = Codes::XXX); | |
141 /** | |
142 * Constructor for setting a monetary value using the native floating-point type. @note The | |
143 * minor units in a floating-point value cannot be verified and will always fit within the | |
144 * currency digits and precision specified, e.g. U.S.A. dollar value with a precision of 0 will | |
145 * store the value 1.555 as 1.55 when no rounder is supplied. | |
146 * | |
147 * @exception overflow_error When the major unit is too large to be stored in a positive direction. | |
148 * @exception overflow_error When the major unit is too small to be stored in a negative direction. | |
149 * @exception domain_error When the conversion results in the floating-point value dividing by | |
150 * zero. | |
151 * @exception underflow_error When the floating-point value is too small to fit within the | |
152 * FloatingPoint. | |
153 * @exception overflow_error When the floating-point value is too large to fit within the | |
154 * FloatingPoint. | |
155 * @exception domain_error When the conversion results in the floating-point value not being | |
156 * valid. | |
157 * | |
158 * @param[in] value The floating-point value to convert. | |
159 * @param[in] code The ISO 4217 currency code the amount represents. | |
160 * @param[in] rounder The method for rounding the number when converting the floating-point | |
161 * number. | |
162 */ | |
163 Money( | |
164 const FloatingPoint value, | |
165 const Codes code, | |
166 RounderFunction<FloatingPoint>&& rounder = | |
167 [] (const FloatingPoint value, const std::uint8_t) | |
168 { | |
169 return value; | |
170 } | |
171 ); | |
172 Money(const Money& other) =default; | |
173 /** | |
174 * Conversion operator to a native floating-point type. | |
175 * | |
176 * @exception domain_error When the conversion results in the floating-point value dividing by | |
177 * zero. | |
178 * @exception underflow_error When the floating-point value is too small to fit within the | |
179 * FloatingPoint. | |
180 * @exception overflow_error When the floating-point value is too large to fit within the | |
181 * FloatingPoint. | |
182 * @exception domain_error When the conversion results in the floating-point value not being | |
183 * valid. | |
184 */ | |
185 explicit operator FloatingPoint() const; | |
186 ~Money() =default; | |
187 /** | |
188 * Negation operator. | |
189 * | |
190 * @return The negated value of the current object. | |
191 */ | |
192 Money operator-() const; | |
193 /** | |
194 * Additive operator. When the supplied monetary object is of a different currency, the amount | |
195 * is converted into the current object's currency using the pecunia::currency::converter function. | |
196 * | |
197 * @exception overflow_error When the result of the operation would be too large to store. | |
198 * @exception underflow_error When the result of the operation would be too small to store. | |
199 * | |
200 * @param[in] other The additional value to add to the current object's value. | |
201 * | |
202 * @return A new object containing the summation of the two values. | |
203 */ | |
204 Money operator+(const Money& other) const; | |
205 /** | |
206 * Subtractive operator. When the supplied monetary object is of a different currency, the | |
207 * amount is converted into the current object's currency using the pecunia::currency::converter | |
208 * function. | |
209 * | |
210 * @exception overflow_error When the result of the operation would be too large to store. | |
211 * @exception underflow_error When the result of the operation would be too small to store. | |
212 * | |
213 * @param[in] other The additional value to subtract from the current object's value. | |
214 * | |
215 * @return A new object containing the difference of the two values. | |
216 */ | |
217 Money operator-(const Money& other) const; | |
218 Money operator*(const Money&) const =delete; | |
219 /** | |
220 * Multiplicative operator. | |
221 * | |
222 * @exception overflow_error When the result of the operation would be too large to store. | |
223 * @exception underflow_error When the result of the operation would be too small to store. | |
224 * | |
225 * @param[in] value The additional value to multiply to the current object's value. | |
226 * | |
227 * @return A new object containing the product of the two values. | |
228 */ | |
229 Money operator*(const FloatingPoint value) const; | |
230 /** | |
231 * Multiplicative operator. | |
232 * | |
233 * @exception overflow_error When the result of the operation would be too large to store. | |
234 * @exception underflow_error When the result of the operation would be too small to store. | |
235 * | |
236 * @tparam p The number of digits after a minor unit to guarantee are always correct. | |
237 * @tparam MU The storage type for the minor unit of the monetary value. This type must | |
238 * be an unsigned internal type. | |
239 * @tparam US The internal storage container for the monetary and major value. | |
240 * @tparam FP The floating-point type to accept and convert into. This type must be | |
241 * a floating-point type. | |
242 * | |
243 * @param[in] lhs The amount by which to multiply the supplied object's value. | |
244 * @param[in] rhs The object that is to be multiplied. | |
245 * | |
246 * @return A new object containing the product of the two values. | |
247 */ | |
248 template<std::uint8_t p, typename MU, typename US, typename FP> | |
249 friend Money<p, MU, US, FP> operator*(const FP lhs, const Money<p, MU, US, FP>& rhs); | |
250 /** | |
251 * Division operator. | |
252 * | |
253 * @exception domain_error When the result of the operation would result in a division by zero. | |
254 * | |
255 * @param[in] other The value to divide by the current object's value. | |
256 * | |
257 * @return The result of the division between the two, a ratio. | |
258 */ | |
259 FloatingPoint operator/(const Money& other) const; | |
260 /** | |
261 * Division operator. | |
262 * | |
263 * @exception domain_error When the result of the operation would result in a division by zero. | |
264 * @exception overflow_error When the result of the operation would be too large to store. | |
265 * @exception underflow_error When the result of the operation would be too small to store. | |
266 * | |
267 * @param[in] value The additional value to divide by the current object's value. | |
268 * | |
269 * @return A new object containing the division of the two values. | |
270 */ | |
271 Money operator/(const FloatingPoint value) const; | |
272 Money operator%(const Money&) const =delete; | |
273 /** | |
274 * Modulus operator performed upon the major units of the object's current value. | |
275 * | |
276 * @exception domain_error When the result of the operation would result in a division by zero. | |
277 * | |
278 * @param[in] value The additional value to perform a "mod" on the current object's value. | |
279 * | |
280 * @return The value of the modulus operation of the two values. | |
281 */ | |
282 UnitStorage operator%(const UnitStorage value) const; | |
283 /** | |
284 * Assignment Operator. When the supplied monetary object is of a different currency, the amount | |
285 * is converted into the current object's currency using the pecunia::currency::converter function. Note | |
286 * that only the amount is assigned, not the currency. | |
287 * | |
288 * @param[in] other The object whose amount is to be assigned into the current object. | |
289 * | |
290 * @return The current object with the new amount. | |
291 */ | |
292 Money& operator=(const Money& other); | |
293 /** | |
294 * Less-than Relational Operator. When the supplied monetary object is of a different currency, | |
295 * the amount is converted into the current object's currency using the pecunia::currency::converter | |
296 * function. | |
297 * | |
298 * @param[in] other The other object whose value is to be compared against. | |
299 * | |
300 * @return A value of true with the current object is less than the supplied object, | |
301 * false else-wise. | |
302 */ | |
303 bool operator<(const Money& other) const; | |
304 /** | |
305 * Less-than Relational Operator. The supplied value is expected to be expressed in terms of the | |
306 * current object's issued currency. | |
307 * | |
308 * @param[in] value The other object whose value is to be compared against. | |
309 * | |
310 * @return A value of true with the current object is less than the supplied object, | |
311 * false else-wise. | |
312 */ | |
313 bool operator<(const FloatingPoint value) const; | |
314 /** | |
315 * Less-than-equal Relational Operator. When the supplied monetary object is of a different | |
316 * currency, the amount is converted into the current object's currency using the | |
317 * pecunia::currency::converter function. | |
318 * | |
319 * @param[in] other The other object whose value is to be compared against. | |
320 * | |
321 * @return A value of true with the current object is less than or equal to the supplied object, | |
322 * false else-wise. | |
323 */ | |
324 bool operator<=(const Money& other) const; | |
325 /** | |
326 * Less-than-equal Relational Operator. The supplied value is expected to be expressed in terms | |
327 * of the current object's issued currency. | |
328 * | |
329 * @param[in] value The other object whose value is to be compared against. | |
330 * | |
331 * @return A value of true with the current object is less than or equal to the supplied object, | |
332 * false else-wise. | |
333 */ | |
334 bool operator<=(const FloatingPoint value) const; | |
335 /** | |
336 * Greater-than Relational Operator. When the supplied monetary object is of a different | |
337 * currency, the amount is converted into the current object's currency using the | |
338 * pecunia::currency::converter function. | |
339 * | |
340 * @param[in] other The other object whose value is to be compared against. | |
341 * | |
342 * @return A value of true with the current object is greater than the supplied object, | |
343 * false else-wise. | |
344 */ | |
345 bool operator>(const Money& other) const; | |
346 /** | |
347 * Greater-than Relational Operator. The supplied value is expected to be expressed in terms | |
348 * of the current object's issued currency. | |
349 * | |
350 * @param[in] value The other object whose value is to be compared against. | |
351 * | |
352 * @return A value of true with the current object is greater than the supplied object, | |
353 * false else-wise. | |
354 */ | |
355 bool operator>(const FloatingPoint value) const; | |
356 /** | |
357 * Greater-than-equal Relational Operator. When the supplied monetary object is of a | |
358 * different currency, the amount is converted into the current object's currency using the | |
359 * pecunia::currency::converter function. | |
360 * | |
361 * @param[in] other The other object whose value is to be compared against. | |
362 * | |
363 * @return A value of true with the current object is greater than or equal to the supplied | |
364 * object, false else-wise. | |
365 */ | |
366 bool operator>=(const Money& other) const; | |
367 /** | |
368 * Greater-than-equal Relational Operator. The supplied value is expected to be expressed in terms | |
369 * of the current object's issued currency. | |
370 * | |
371 * @param[in] value The other object whose value is to be compared against. | |
372 * | |
373 * @return A value of true with the current object is greater than or equal to the supplied | |
374 * object, false else-wise. | |
375 */ | |
376 bool operator>=(const FloatingPoint value) const; | |
377 /** | |
378 * Equality Operator. When the supplied monetary object is of a different currency, | |
379 * the amount is converted into the current object's currency using the pecunia::currency::converter | |
380 * function. | |
381 * | |
382 * @param[in] other The other object whose value is to be compared against. | |
383 * | |
384 * @return A value of true with the current object is equal to the supplied object, | |
385 * false else-wise. | |
386 */ | |
387 bool operator==(const Money& other) const; | |
388 /** | |
389 * Inequality Operator. When the supplied monetary object is of a different currency, | |
390 * the amount is converted into the current object's currency using the pecunia::currency::converter | |
391 * function. | |
392 * | |
393 * @param[in] other The other object whose value is to be compared against. | |
394 * | |
395 * @return A value of true with the current object is not equal to the supplied object, | |
396 * false else-wise. | |
397 */ | |
398 bool operator!=(const Money& other) const; | |
399 /** | |
400 * Additive assignment operator. When the supplied monetary object is of a different currency, | |
401 * the amount is converted into the current object's currency using the pecunia::currency::converter | |
402 * function. | |
403 * | |
404 * @exception overflow_error When the result of the operation would be too large to store. | |
405 * @exception underflow_error When the result of the operation would be too small to store. | |
406 * | |
407 * @param[in] other The additional value to add to the current object. | |
408 * | |
409 * @return The current object containing the summation of the two values. | |
410 */ | |
411 Money& operator+=(const Money& other); | |
412 /** | |
413 * Subtractive assignment operator. When the supplied monetary object is of a different | |
414 * currency, the amount is converted into the current object's currency using the | |
415 * pecunia::currency::converter function. | |
416 * | |
417 * @exception overflow_error When the result of the operation would be too large to store. | |
418 * @exception underflow_error When the result of the operation would be too small to store. | |
419 * | |
420 * @param[in] other The additional value to subtract from the current object. | |
421 * | |
422 * @return The current object containing the difference of the two values. | |
423 */ | |
424 Money& operator-=(const Money& other); | |
425 /** | |
426 * Multiplicative assignment operator. | |
427 * | |
428 * @exception overflow_error When the result of the operation would be too large to store. | |
429 * @exception underflow_error When the result of the operation would be too small to store. | |
430 * | |
431 * @param[in] value The additional value to multiply to the current object. | |
432 * | |
433 * @return The current object containing the product of the two values. | |
434 */ | |
435 Money& operator*=(const FloatingPoint value); | |
436 /** | |
437 * Division assignment operator. | |
438 * | |
439 * @exception domain_error When the result of the operation would result in a division by zero. | |
440 * @exception overflow_error When the result of the operation would be too large to store. | |
441 * @exception underflow_error When the result of the operation would be too small to store. | |
442 * | |
443 * @param[in] value The additional value to divide by the current object. | |
444 * | |
445 * @return The current object containing the division of the two values. | |
446 */ | |
447 Money& operator/=(const FloatingPoint value); | |
448 /** | |
449 * Stream Insertion Operator. By default the form will be the locale's monetary format with no | |
450 * spaces, the ISO code, and trailing the value, e.g. a U.S.A. locale with value of one dollar | |
451 * and twenty-three cents would appear as 1.23USD. | |
452 * | |
453 * @tparam p The number of digits after a minor unit to guarantee are always correct. | |
454 * @tparam MU The storage type for the minor unit of the monetary value. This type must | |
455 * be an unsigned internal type. | |
456 * @tparam US The internal storage container for the monetary and major value. | |
457 * @tparam FP The floating-point type to accept and convert into. This type must be | |
458 * a floating-point type. | |
459 * | |
460 * @param[in,out] stream The destination source for the contents of the supplied money object. | |
461 * @param[in] m The object whose values are to placed into the destination source. | |
462 * | |
463 * @return The modified source stream object filled with the contents of the money object. | |
464 */ | |
465 template<std::uint8_t p, typename MU, typename US, typename FP> | |
466 friend std::ostream& operator<<(std::ostream& stream, const Money<p, MU, US, FP>& m); | |
467 /** | |
468 * Stream Extraction Operator. The monetary amount will be read in using the locale's setting | |
469 * for reading in money. The currency code is required and must be in all upper-case letters. | |
470 * | |
471 * @pre The locale of the supplied stream must be set to get the money formatted in readable | |
472 * manner. | |
473 * | |
474 * @exception overflow_error When the major unit is too large to be stored in a positive direction. | |
475 * @exception overflow_error When the major unit is too small to be stored in a negative direction. | |
476 * @exception domain_error When the conversion results in the floating-point value dividing by | |
477 * zero. | |
478 * @exception underflow_error When the floating-point value is too small to fit within the | |
479 * FloatingPoint. | |
480 * @exception overflow_error When the floating-point value is too large to fit within the | |
481 * FloatingPoint. | |
482 * @exception domain_error When the conversion results in the floating-point value not being | |
483 * valid. | |
484 * | |
485 * @tparam p The number of digits after a minor unit to guarantee are always correct. | |
486 * @tparam MU The storage type for the minor unit of the monetary value. This type must | |
487 * be an unsigned internal type. | |
488 * @tparam US The internal storage container for the monetary and major value. | |
489 * @tparam FP The floating-point type to accept and convert into. This type must be | |
490 * a floating-point type. | |
491 * | |
492 * @param[in,out] stream The extraction source for the contents of the supplied money object. | |
493 * @param[out] m The object whose values are to be filled by the contents of the stream. | |
494 * | |
495 * @return The modified source stream object emptied of a money entry. | |
496 */ | |
497 template<std::uint8_t p, typename MU, typename US, typename FP> | |
498 friend std::istream& operator>>(std::istream& stream, Money<p, MU, US, FP>& m); | |
499 /** | |
500 * Accessor to the major monetary value, e.g. only the U.S.A. dollars. | |
501 */ | |
502 UnitStorage major() const; | |
503 /** | |
504 * Accessor to the minor monetary value, e.g. only the U.S.A. cents. | |
505 */ | |
506 MinorUnit minor() const; | |
507 /** | |
508 * Accessor to the code the currency amount represents. | |
509 */ | |
510 Codes code() const; | |
511 /** | |
512 * Performs a test to determine if the stored value is positive. | |
513 * | |
514 * @return True when the value is greater than zero. | |
515 */ | |
516 bool isPositive() const; | |
517 /** | |
518 * @brief Converts the monetary value into a native floating-point type using a supplied | |
519 * rounding method. When a rounding function is not supplied, no rounding will occur. | |
520 * | |
521 * @param[in] rounder The rounding function to apply to the monetary value. | |
522 * @param[in] digits The number of digits after the major unit. | |
523 * | |
524 * @exception domain_error When the conversion results in the floating-point value dividing by | |
525 * zero. | |
526 * @exception underflow_error When the floating-point value is too small to fit within the | |
527 * FloatingPoint. | |
528 * @exception overflow_error When the floating-point value is too large to fit within the | |
529 * FloatingPoint. | |
530 * @exception domain_error When the conversion results in the floating-point value not being | |
531 * valid. | |
532 */ | |
533 FloatingPoint number( | |
534 RounderFunction<FloatingPoint>&& rounder = | |
535 [] (const FloatingPoint value, const std::uint8_t) | |
536 { | |
537 return value; | |
538 }, | |
539 const std::uint8_t digits = precision | |
540 ) const; | |
541 /** | |
542 * @brief Determines if the instance has an amount set. For example, an amount of 2.34 USD would | |
543 * be set, but 0.00 XXX would not be. | |
544 * | |
545 * @return When the major, minor, and code are all set gives true, false else-wise. | |
546 */ | |
547 bool hasAmount() const; | |
548 /** | |
549 * @brief Changes the ISO 4217 currency code without performing any conversions. | |
550 * | |
551 * @param[in] code The new ISO 4217 currency code the amount represents. | |
552 */ | |
553 void setCode(const pecunia::currency::Codes& code); | |
554 /** | |
555 * @brief Calculates the largest monetary value that can be used for a given currency. | |
556 * | |
557 * @param code The currency code to calculate the monetary value in. | |
558 * | |
559 * @return The monetary value in the form MMM.mmpp. | |
560 */ | |
561 static FloatingPoint maximum(const pecunia::currency::Codes& code); | |
562 /** | |
563 * @brief Calculates the smallest monetary value that can be used for a given currency. | |
564 * | |
565 * @param code The currency code to calculate the monetary value in. | |
566 * | |
567 * @return The monetary value in the form MMM.mmpp. | |
568 */ | |
569 static FloatingPoint minimum(const pecunia::currency::Codes& code); | |
570 }; | |
571 | |
572 #ifndef DOXYGEN_SHOULD_SKIP_THIS | |
573 | |
574 template<std::uint8_t p, typename MU, typename US, typename FP> | |
575 Money<p, MU, US, FP> operator*(const FP lhs, const Money<p, MU, US, FP>& rhs); | |
576 | |
577 template<std::uint8_t p, typename MU, typename US, typename FP> | |
578 std::ostream& operator<<(std::ostream& stream, const Money<p, MU, US, FP>& m); | |
579 | |
580 template<std::uint8_t p, typename MU, typename US, typename FP> | |
581 std::istream& operator>>(std::istream& stream, Money<p, MU, US, FP>& m); | |
582 | |
583 #endif | |
584 | |
585 /** | |
586 * @brief Converts the montary representation into a string from. The format of the string | |
587 * representation is the amount followed by a space and the currency code. | |
588 * | |
589 * @tparam p The number of digits after a minor unit to guarantee are always correct. | |
590 * @tparam MU The storage type for the minor unit of the monetary value. This type must | |
591 * be an unsigned internal type. | |
592 * @tparam US The internal storage container for the monetary and major value. | |
593 * @tparam FP The floating-point type to accept and convert into. This type must be | |
594 * a floating-point type. | |
595 * | |
596 * @param[out] m The object whose values are to be converted into a string form. | |
597 * | |
598 * @return The string representation. | |
599 */ | |
600 template<std::uint8_t p, typename MU, typename US, typename FP> | |
601 std::string to_string(const Money< p, MU, US, FP >& m); | |
602 | |
603 }} | |
604 | |
605 template | |
606 < | |
607 std::uint8_t precision, | |
608 typename MinorUnit, | |
609 typename UnitStorage, | |
610 typename FloatingPoint | |
611 > | |
612 UnitStorage | |
613 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::maximumMajorValue() | |
614 { | |
615 UnitStorage rawMaxMajor{ | |
616 static_cast<UnitStorage>( | |
617 std::numeric_limits<UnitStorage>::max() - this->maximumMinorValue() | |
618 ) | |
619 }; | |
620 const std::int32_t ratio{minorUnitPrecisionRatio(this->iso4217Code_, precision)}; | |
621 UnitStorage maxMajor{static_cast<UnitStorage>(rawMaxMajor / ratio)}; | |
622 return maxMajor; | |
623 } | |
624 | |
625 template | |
626 < | |
627 std::uint8_t precision, | |
628 typename MinorUnit, | |
629 typename UnitStorage, | |
630 typename FloatingPoint | |
631 > | |
632 UnitStorage | |
633 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::minimumMajorValue() | |
634 { | |
635 const auto maxMinorValue{this->maximumMinorValue()}; | |
636 UnitStorage rawMinMajor{ | |
637 static_cast<UnitStorage>(std::numeric_limits<UnitStorage>::lowest() + maxMinorValue) | |
638 }; | |
639 const auto minorPrecisionRatio{minorUnitPrecisionRatio(this->iso4217Code_, precision)}; | |
640 UnitStorage minMajor{static_cast<UnitStorage>(rawMinMajor / minorPrecisionRatio)}; | |
641 return minMajor; | |
642 } | |
643 | |
644 template | |
645 < | |
646 std::uint8_t precision, | |
647 typename MinorUnit, | |
648 typename UnitStorage, | |
649 typename FloatingPoint | |
650 > | |
651 MinorUnit | |
652 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::maximumMinorValue() | |
653 { | |
654 const std::int32_t digits{minorUnitDigits(this->iso4217Code_)}; | |
655 const MinorUnit max{static_cast<MinorUnit>(pow(10, digits + precision) - 1.0)}; | |
656 return max; | |
657 } | |
658 | |
659 template | |
660 < | |
661 std::uint8_t precision, | |
662 typename MinorUnit, | |
663 typename UnitStorage, | |
664 typename FloatingPoint | |
665 > | |
666 UnitStorage | |
667 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::maximumAmountValue() | |
668 { | |
669 const std::int32_t unitRatio{minorUnitPrecisionRatio(this->iso4217Code_, precision)}; | |
670 const UnitStorage max{ | |
671 static_cast<UnitStorage>( | |
672 (this->maximumMajorValue() * unitRatio) + this->maximumMinorValue() | |
673 ) | |
674 }; | |
675 return max; | |
676 } | |
677 | |
678 template | |
679 < | |
680 std::uint8_t precision, | |
681 typename MinorUnit, | |
682 typename UnitStorage, | |
683 typename FloatingPoint | |
684 > | |
685 UnitStorage | |
686 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::minimumAmountValue() | |
687 { | |
688 const std::int32_t unitRatio{minorUnitPrecisionRatio(this->iso4217Code_, precision)}; | |
689 const auto minimumMajorValue{this->minimumMajorValue()}; | |
690 const auto maximumMinorValue{this->maximumMinorValue()}; | |
691 const UnitStorage min{ | |
692 static_cast<UnitStorage>((minimumMajorValue * unitRatio) - maximumMinorValue) | |
693 }; | |
694 return min; | |
695 } | |
696 | |
697 template | |
698 < | |
699 std::uint8_t precision, | |
700 typename MinorUnit, | |
701 typename UnitStorage, | |
702 typename FloatingPoint | |
703 > | |
704 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::Money(const Codes code) : | |
705 Money{0, 0, code} | |
706 {} | |
707 | |
708 template | |
709 < | |
710 std::uint8_t precision, | |
711 typename MinorUnit, | |
712 typename UnitStorage, | |
713 typename FloatingPoint | |
714 > | |
715 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::Money( | |
716 const MinorUnit minor, | |
717 const MinorSign sign, | |
718 const Codes code | |
719 ) : | |
720 Money{0, minor, code} | |
721 { | |
722 assert( | |
723 (minor > 0 || (minor == 0 && sign == MinorSign::Neither)) | |
724 && "The value of zero has no sign." | |
725 ); | |
726 | |
727 if (sign == MinorSign::Negative) | |
728 this->amount_ *= -1; | |
729 } | |
730 | |
731 template | |
732 < | |
733 std::uint8_t precision, | |
734 typename MinorUnit, | |
735 typename UnitStorage, | |
736 typename FloatingPoint | |
737 > | |
738 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::Money( | |
739 const UnitStorage major, | |
740 const MinorUnit minor, | |
741 const Codes code | |
742 ) : | |
743 amount_{0}, | |
744 iso4217Code_{code} | |
745 { | |
746 assert( | |
747 pow(10, minorUnitDigits(code) + precision) - 1.0 <= std::numeric_limits<MinorUnit>::max() | |
748 && "The number of digits with precision is too large and cannot fit inside the minor type." | |
749 ); | |
750 | |
751 const std::int32_t unitRatio{minorUnitPrecisionRatio(code, precision)}; | |
752 | |
753 if (major > this->maximumMajorValue()) | |
754 throw std::overflow_error{ | |
755 "The major money value is too large to store within the supplied precision." | |
756 }; | |
757 | |
758 if (major < this->minimumMajorValue()) | |
759 throw std::overflow_error{ | |
760 "The major money value is too small to store within the supplied precision." | |
761 }; | |
762 | |
763 if (minor > this->maximumMinorValue()) | |
764 throw std::overflow_error{ | |
765 "The minor money value is too large to store within the supplied precision." | |
766 }; | |
767 | |
768 if (major >= 0) | |
769 this->amount_ = (major * unitRatio) + minor; | |
770 else | |
771 this->amount_ = (major * unitRatio) - minor; | |
772 } | |
773 | |
774 template | |
775 < | |
776 std::uint8_t precision, | |
777 typename MinorUnit, | |
778 typename UnitStorage, | |
779 typename FloatingPoint | |
780 > | |
781 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::Money( | |
782 const FloatingPoint value, | |
783 const Codes code, | |
784 RounderFunction<FloatingPoint>&& rounder | |
785 ) : | |
786 Money{ | |
787 fromFloatingPointMajor<FloatingPoint, UnitStorage>( | |
788 value, | |
789 code, | |
790 precision, | |
791 rounder | |
792 ), | |
793 fromFloatingPointMinor<FloatingPoint, MinorUnit, UnitStorage>( | |
794 value, | |
795 code, | |
796 precision, | |
797 rounder | |
798 ), | |
799 code | |
800 } | |
801 { | |
802 if (this->amount_ == 0 && ! internal::equalWithinEpsilon(value, 0.0)) | |
803 throw std::underflow_error{ | |
804 "The value " + std::to_string(value) | |
805 + " is too small to store within the supplied precision." | |
806 }; | |
807 } | |
808 | |
809 template | |
810 < | |
811 std::uint8_t precision, | |
812 typename MinorUnit, | |
813 typename UnitStorage, | |
814 typename FloatingPoint | |
815 > | |
816 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator FloatingPoint() const | |
817 { | |
818 return this->number(); | |
819 } | |
820 | |
821 template | |
822 < | |
823 std::uint8_t precision, | |
824 typename MinorUnit, | |
825 typename UnitStorage, | |
826 typename FloatingPoint | |
827 > | |
828 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint> | |
829 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator-() const | |
830 { | |
831 Money m{*this}; | |
832 m.amount_ *= -1; | |
833 return m; | |
834 } | |
835 | |
836 template | |
837 < | |
838 std::uint8_t precision, | |
839 typename MinorUnit, | |
840 typename UnitStorage, | |
841 typename FloatingPoint | |
842 > | |
843 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint> | |
844 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator+( | |
845 const Money& other | |
846 ) const | |
847 { | |
848 Money m{*this}; | |
849 m += other; | |
850 return m; | |
851 } | |
852 | |
853 template | |
854 < | |
855 std::uint8_t precision, | |
856 typename MinorUnit, | |
857 typename UnitStorage, | |
858 typename FloatingPoint | |
859 > | |
860 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint> | |
861 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator-( | |
862 const Money& other | |
863 ) const | |
864 { | |
865 Money m{*this}; | |
866 m -= other; | |
867 return m; | |
868 } | |
869 | |
870 template | |
871 < | |
872 std::uint8_t precision, | |
873 typename MinorUnit, | |
874 typename UnitStorage, | |
875 typename FloatingPoint | |
876 > | |
877 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint> | |
878 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator*( | |
879 const FloatingPoint value | |
880 ) const | |
881 { | |
882 Money m{*this}; | |
883 m *= value; | |
884 return m; | |
885 } | |
886 | |
887 template | |
888 < | |
889 std::uint8_t precision, | |
890 typename MinorUnit, | |
891 typename UnitStorage, | |
892 typename FloatingPoint | |
893 > | |
894 FloatingPoint pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator/( | |
895 const Money& other | |
896 ) const | |
897 { | |
898 if (other.amount_ == 0) | |
899 throw std::domain_error{ | |
900 "The division operation of " + std::to_string(this->amount_) + " and " | |
901 + std::to_string(other.amount_) + " result is undefined." | |
902 }; | |
903 const UnitStorage normalisedAmount{ | |
904 normaliseAmount<UnitStorage, FloatingPoint>( | |
905 other.amount_, | |
906 other.iso4217Code_, | |
907 this->iso4217Code_ | |
908 ) | |
909 }; | |
910 return this->amount_ / static_cast<FloatingPoint>(normalisedAmount); | |
911 } | |
912 | |
913 template | |
914 < | |
915 std::uint8_t precision, | |
916 typename MinorUnit, | |
917 typename UnitStorage, | |
918 typename FloatingPoint | |
919 > | |
920 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint> | |
921 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator/( | |
922 const FloatingPoint value | |
923 ) const | |
924 { | |
925 Money m{*this}; | |
926 m /= value; | |
927 return m; | |
928 } | |
929 | |
930 template | |
931 < | |
932 std::uint8_t precision, | |
933 typename MinorUnit, | |
934 typename UnitStorage, | |
935 typename FloatingPoint | |
936 > | |
937 UnitStorage pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator%( | |
938 const UnitStorage value | |
939 ) const | |
940 { | |
941 if (value == 0) | |
942 throw std::domain_error{ | |
943 "The modulus operation of " + std::to_string(this->amount_) + " and " | |
944 + std::to_string(value) + " result is undefined." | |
945 }; | |
946 return this->major() % value; | |
947 } | |
948 | |
949 template | |
950 < | |
951 std::uint8_t precision, | |
952 typename MinorUnit, | |
953 typename UnitStorage, | |
954 typename FloatingPoint | |
955 > | |
956 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>& | |
957 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator=( | |
958 const Money<precision, MinorUnit, UnitStorage, FloatingPoint>& other | |
959 ) | |
960 { | |
961 if (this != &other) | |
962 { | |
963 const UnitStorage normalisedAmount{ | |
964 normaliseAmount<UnitStorage, FloatingPoint>( | |
965 other.amount_, | |
966 other.iso4217Code_, | |
967 this->iso4217Code_ | |
968 ) | |
969 }; | |
970 const auto maximumAmountValue{this->maximumAmountValue()}; | |
971 const auto minimumAmountValue{this->minimumAmountValue()}; | |
972 | |
973 if (normalisedAmount <= maximumAmountValue && normalisedAmount >= minimumAmountValue) | |
974 this->amount_ = normalisedAmount; | |
975 else | |
976 throw std::overflow_error{ | |
977 "The assignment operation of " + std::to_string(other.amount_) + " to " | |
978 + std::to_string(this->amount_) + " is too large to fit within the precision." | |
979 }; | |
980 } | |
981 return *this; | |
982 } | |
983 | |
984 template | |
985 < | |
986 std::uint8_t precision, | |
987 typename MinorUnit, | |
988 typename UnitStorage, | |
989 typename FloatingPoint | |
990 > | |
991 bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator<( | |
992 const Money& other | |
993 ) const | |
994 { | |
995 try | |
996 { | |
997 const UnitStorage normalisedAmount{ | |
998 normaliseAmount<UnitStorage, FloatingPoint>( | |
999 other.amount_, | |
1000 other.iso4217Code_, | |
1001 this->iso4217Code_ | |
1002 ) | |
1003 }; | |
1004 return this->amount_ < normalisedAmount; | |
1005 } | |
1006 catch (const std::logic_error&) | |
1007 { | |
1008 throw; | |
1009 } | |
1010 catch (const std::exception&) | |
1011 { | |
1012 if (other.amount_ > 0) // other.amount_ is too large positive. | |
1013 return true; | |
1014 | |
1015 if (other.amount_ < 0) // other.amount_ is too large negative. | |
1016 return false; | |
1017 throw; // Something strange has occurred. | |
1018 } | |
1019 } | |
1020 | |
1021 template | |
1022 < | |
1023 std::uint8_t precision, | |
1024 typename MinorUnit, | |
1025 typename UnitStorage, | |
1026 typename FloatingPoint | |
1027 > | |
1028 bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator<( | |
1029 const FloatingPoint value | |
1030 ) const | |
1031 { | |
1032 return *this < Money{value, this->iso4217Code_}; | |
1033 } | |
1034 | |
1035 template | |
1036 < | |
1037 std::uint8_t precision, | |
1038 typename MinorUnit, | |
1039 typename UnitStorage, | |
1040 typename FloatingPoint | |
1041 > | |
1042 bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator<=( | |
1043 const Money& other | |
1044 ) const | |
1045 { | |
1046 return (*this < other) || (*this == other); | |
1047 } | |
1048 | |
1049 template | |
1050 < | |
1051 std::uint8_t precision, | |
1052 typename MinorUnit, | |
1053 typename UnitStorage, | |
1054 typename FloatingPoint | |
1055 > | |
1056 bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator<=( | |
1057 const FloatingPoint value | |
1058 ) const | |
1059 { | |
1060 return *this <= Money{value, this->iso4217Code_}; | |
1061 } | |
1062 | |
1063 template | |
1064 < | |
1065 std::uint8_t precision, | |
1066 typename MinorUnit, | |
1067 typename UnitStorage, | |
1068 typename FloatingPoint | |
1069 > | |
1070 bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator>( | |
1071 const Money& other | |
1072 ) const | |
1073 { | |
1074 try | |
1075 { | |
1076 const UnitStorage normalisedAmount{ | |
1077 normaliseAmount<UnitStorage, FloatingPoint>( | |
1078 other.amount_, | |
1079 other.iso4217Code_, | |
1080 this->iso4217Code_ | |
1081 ) | |
1082 }; | |
1083 return this->amount_ > normalisedAmount; | |
1084 } | |
1085 catch (const std::logic_error&) | |
1086 { | |
1087 throw; | |
1088 } | |
1089 catch (const std::exception&) | |
1090 { | |
1091 if (other.amount_ > 0) // other.amount_ is too large positive. | |
1092 return false; | |
1093 | |
1094 if (other.amount_ < 0) // other.amount_ is too large negative. | |
1095 return true; | |
1096 throw; // Something strange has occurred. | |
1097 } | |
1098 } | |
1099 | |
1100 template | |
1101 < | |
1102 std::uint8_t precision, | |
1103 typename MinorUnit, | |
1104 typename UnitStorage, | |
1105 typename FloatingPoint | |
1106 > | |
1107 bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator>( | |
1108 const FloatingPoint value | |
1109 ) const | |
1110 { | |
1111 return *this > Money{value, this->iso4217Code_}; | |
1112 } | |
1113 | |
1114 template | |
1115 < | |
1116 std::uint8_t precision, | |
1117 typename MinorUnit, | |
1118 typename UnitStorage, | |
1119 typename FloatingPoint | |
1120 > | |
1121 bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator>=( | |
1122 const Money& other | |
1123 ) const | |
1124 { | |
1125 return (*this > other) || (*this == other); | |
1126 } | |
1127 | |
1128 template | |
1129 < | |
1130 std::uint8_t precision, | |
1131 typename MinorUnit, | |
1132 typename UnitStorage, | |
1133 typename FloatingPoint | |
1134 > | |
1135 bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator>=( | |
1136 const FloatingPoint value | |
1137 ) const | |
1138 { | |
1139 return *this >= Money{value, this->iso4217Code_}; | |
1140 } | |
1141 | |
1142 template | |
1143 < | |
1144 std::uint8_t precision, | |
1145 typename MinorUnit, | |
1146 typename UnitStorage, | |
1147 typename FloatingPoint | |
1148 > | |
1149 bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator==( | |
1150 const Money& other | |
1151 ) const | |
1152 { | |
1153 try | |
1154 { | |
1155 const UnitStorage normalisedAmount{ | |
1156 normaliseAmount<UnitStorage, FloatingPoint>( | |
1157 other.amount_, | |
1158 other.iso4217Code_, | |
1159 this->iso4217Code_ | |
1160 ) | |
1161 }; | |
1162 return this->amount_ == normalisedAmount; | |
1163 } | |
1164 catch (const std::exception&) | |
1165 { | |
1166 // Doesn't matter why we threw, e.g. overflow, underflow, different codes, the error itself | |
1167 // is enough to know that the two values are not equal. | |
1168 return false; | |
1169 } | |
1170 } | |
1171 | |
1172 template | |
1173 < | |
1174 std::uint8_t precision, | |
1175 typename MinorUnit, | |
1176 typename UnitStorage, | |
1177 typename FloatingPoint | |
1178 > | |
1179 bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator!=( | |
1180 const Money& other | |
1181 ) const | |
1182 { | |
1183 return ! (*this == other); | |
1184 } | |
1185 | |
1186 template | |
1187 < | |
1188 std::uint8_t precision, | |
1189 typename MinorUnit, | |
1190 typename UnitStorage, | |
1191 typename FloatingPoint | |
1192 > | |
1193 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>& | |
1194 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator+=( | |
1195 const Money& other | |
1196 ) | |
1197 { | |
1198 const UnitStorage normalisedAmount{ | |
1199 normaliseAmount<UnitStorage, FloatingPoint>( | |
1200 other.amount_, | |
1201 other.iso4217Code_, | |
1202 this->iso4217Code_ | |
1203 ) | |
1204 }; | |
1205 const internal::VerifiedValue verified{ | |
1206 internal::verifyAdditionFits( | |
1207 this->amount_, | |
1208 normalisedAmount, | |
1209 this->maximumAmountValue(), | |
1210 this->minimumAmountValue() | |
1211 ) | |
1212 }; | |
1213 switch (verified) | |
1214 { | |
1215 case internal::VerifiedValue::Fits: | |
1216 this->amount_ += normalisedAmount; | |
1217 return *this; | |
1218 case internal::VerifiedValue::Overflows: | |
1219 throw std::overflow_error{ | |
1220 "The addition operation of " + std::to_string(this->amount_) + ' ' | |
1221 + toIso4217(this->iso4217Code_) + " and " + std::to_string(other.amount_) + ' ' | |
1222 + toIso4217(other.iso4217Code_) + " cannot be safely stored." | |
1223 }; | |
1224 case internal::VerifiedValue::Underflows: | |
1225 throw std::underflow_error{ | |
1226 "The addition operation of " + std::to_string(this->amount_) + ' ' | |
1227 + toIso4217(this->iso4217Code_) + " and " + std::to_string(other.amount_) + ' ' | |
1228 + toIso4217(other.iso4217Code_) + " cannot be safely stored." | |
1229 }; | |
1230 case internal::VerifiedValue::Undefined: | |
1231 default: | |
1232 throw std::logic_error{"Incorrect addition verification result."}; | |
1233 }; | |
1234 } | |
1235 | |
1236 template | |
1237 < | |
1238 std::uint8_t precision, | |
1239 typename MinorUnit, | |
1240 typename UnitStorage, | |
1241 typename FloatingPoint | |
1242 > | |
1243 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>& | |
1244 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator-=( | |
1245 const Money& other | |
1246 ) | |
1247 { | |
1248 const UnitStorage normalisedAmount{ | |
1249 normaliseAmount<UnitStorage, FloatingPoint>( | |
1250 other.amount_, | |
1251 other.iso4217Code_, | |
1252 this->iso4217Code_ | |
1253 ) | |
1254 }; | |
1255 const internal::VerifiedValue verified{ | |
1256 internal::verifySubtractionFits( | |
1257 this->amount_, | |
1258 normalisedAmount, | |
1259 this->maximumAmountValue(), | |
1260 this->minimumAmountValue() | |
1261 ) | |
1262 }; | |
1263 switch (verified) | |
1264 { | |
1265 case internal::VerifiedValue::Fits: | |
1266 this->amount_ -= normalisedAmount; | |
1267 return *this; | |
1268 case internal::VerifiedValue::Overflows: | |
1269 throw std::overflow_error{ | |
1270 "The subtraction operation of " + std::to_string(this->amount_) + ' ' | |
1271 + toIso4217(this->iso4217Code_) + " and " + std::to_string(other.amount_) + ' ' | |
1272 + toIso4217(other.iso4217Code_) + " cannot be safely stored." | |
1273 }; | |
1274 case internal::VerifiedValue::Underflows: | |
1275 throw std::underflow_error{ | |
1276 "The subtraction operation of " + std::to_string(this->amount_) + ' ' | |
1277 + toIso4217(this->iso4217Code_) + " and " + std::to_string(other.amount_) + ' ' | |
1278 + toIso4217(other.iso4217Code_) + " cannot be safely stored." | |
1279 }; | |
1280 case internal::VerifiedValue::Undefined: | |
1281 default: | |
1282 throw std::logic_error{"Incorrect subtraction verification result."}; | |
1283 }; | |
1284 } | |
1285 | |
1286 template | |
1287 < | |
1288 std::uint8_t precision, | |
1289 typename MinorUnit, | |
1290 typename UnitStorage, | |
1291 typename FloatingPoint | |
1292 > | |
1293 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>& | |
1294 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator*=( | |
1295 const FloatingPoint value | |
1296 ) | |
1297 { | |
1298 const internal::VerifiedValue verified{ | |
1299 internal::verifyMultiplicationFits( | |
1300 this->amount_, | |
1301 value, | |
1302 this->maximumAmountValue(), | |
1303 this->minimumAmountValue(), | |
1304 this->maximumMinorValue() | |
1305 ) | |
1306 }; | |
1307 switch (verified) | |
1308 { | |
1309 case internal::VerifiedValue::Fits: | |
1310 this->amount_ = static_cast<UnitStorage>(this->amount_ * value); | |
1311 return *this; | |
1312 case internal::VerifiedValue::Overflows: | |
1313 throw std::overflow_error{ | |
1314 "The multiplication operation of " + std::to_string(this->amount_) + " and " | |
1315 + std::to_string(value) + " cannot be safely stored." | |
1316 }; | |
1317 case internal::VerifiedValue::Underflows: | |
1318 throw std::underflow_error{ | |
1319 "The multiplication operation of " + std::to_string(this->amount_) + " and " | |
1320 + std::to_string(value) + " cannot be safely stored." | |
1321 }; | |
1322 case internal::VerifiedValue::Undefined: | |
1323 default: | |
1324 throw std::logic_error{"Incorrect multiplication verification result."}; | |
1325 }; | |
1326 } | |
1327 | |
1328 template | |
1329 < | |
1330 std::uint8_t precision, | |
1331 typename MinorUnit, | |
1332 typename UnitStorage, | |
1333 typename FloatingPoint | |
1334 > | |
1335 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>& | |
1336 pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::operator/=( | |
1337 const FloatingPoint value | |
1338 ) | |
1339 { | |
1340 const internal::VerifiedValue verified{ | |
1341 internal::verifyDivisionFits( | |
1342 this->amount_, | |
1343 value, | |
1344 this->maximumAmountValue(), | |
1345 this->minimumAmountValue(), | |
1346 this->maximumMinorValue() | |
1347 ) | |
1348 }; | |
1349 switch (verified) | |
1350 { | |
1351 case internal::VerifiedValue::Fits: | |
1352 this->amount_ = static_cast<UnitStorage>(this->amount_ / value); | |
1353 return *this; | |
1354 case internal::VerifiedValue::Overflows: | |
1355 throw std::overflow_error{ | |
1356 "The division operation of " + std::to_string(this->amount_) + " and " | |
1357 + std::to_string(value) + " cannot be safely stored." | |
1358 }; | |
1359 case internal::VerifiedValue::Undefined: | |
1360 throw std::domain_error{ | |
1361 "The division operation of " + std::to_string(this->amount_) + " and " | |
1362 + std::to_string(value) + " result is undefined." | |
1363 }; | |
1364 case internal::VerifiedValue::Underflows: | |
1365 throw std::underflow_error{ | |
1366 "The division operation of " + std::to_string(this->amount_) + " and " | |
1367 + std::to_string(value) + " cannot be safely stored." | |
1368 }; | |
1369 default: | |
1370 throw std::logic_error{"Incorrect division verification result."}; | |
1371 }; | |
1372 } | |
1373 | |
1374 template | |
1375 < | |
1376 std::uint8_t precision, | |
1377 typename MinorUnit, | |
1378 typename UnitStorage, | |
1379 typename FloatingPoint | |
1380 > | |
1381 UnitStorage pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::major() const | |
1382 { | |
1383 return this->amount_ / minorUnitPrecisionRatio(this->iso4217Code_, precision); | |
1384 } | |
1385 | |
1386 template | |
1387 < | |
1388 std::uint8_t precision, | |
1389 typename MinorUnit, | |
1390 typename UnitStorage, | |
1391 typename FloatingPoint | |
1392 > | |
1393 MinorUnit pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::minor() const | |
1394 { | |
1395 return static_cast<MinorUnit>( | |
1396 std::abs(this->amount_ % minorUnitPrecisionRatio(this->iso4217Code_, precision)) | |
1397 ); | |
1398 } | |
1399 | |
1400 template | |
1401 < | |
1402 std::uint8_t precision, | |
1403 typename MinorUnit, | |
1404 typename UnitStorage, | |
1405 typename FloatingPoint | |
1406 > | |
1407 pecunia::currency::Codes pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::code() const | |
1408 { | |
1409 return this->iso4217Code_; | |
1410 } | |
1411 | |
1412 template | |
1413 < | |
1414 std::uint8_t precision, | |
1415 typename MinorUnit, | |
1416 typename UnitStorage, | |
1417 typename FloatingPoint | |
1418 > | |
1419 bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::isPositive() const | |
1420 { | |
1421 return this->amount_ > 0; | |
1422 } | |
1423 | |
1424 template | |
1425 < | |
1426 std::uint8_t precision, | |
1427 typename MinorUnit, | |
1428 typename UnitStorage, | |
1429 typename FloatingPoint | |
1430 > | |
1431 FloatingPoint pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::number( | |
1432 RounderFunction<FloatingPoint>&& rounder, | |
1433 const std::uint8_t digits | |
1434 ) const | |
1435 { | |
1436 FloatingPoint native{static_cast<FloatingPoint>(this->amount_)}; | |
1437 native /= minorUnitPrecisionRatio(this->iso4217Code_, precision); | |
1438 return rounder(internal::verifiedFloatingPoint<FloatingPoint>(native), digits); | |
1439 } | |
1440 | |
1441 template | |
1442 < | |
1443 std::uint8_t precision, | |
1444 typename MinorUnit, | |
1445 typename UnitStorage, | |
1446 typename FloatingPoint | |
1447 > | |
1448 bool pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::hasAmount() const | |
1449 { | |
1450 return this->amount_ != 0 && this->iso4217Code_ != Codes::XXX; | |
1451 } | |
1452 | |
1453 template | |
1454 < | |
1455 std::uint8_t precision, | |
1456 typename MinorUnit, | |
1457 typename UnitStorage, | |
1458 typename FloatingPoint | |
1459 > | |
1460 void pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::setCode( | |
1461 const pecunia::currency::Codes& code | |
1462 ) | |
1463 { | |
1464 this->iso4217Code_ = code; | |
1465 } | |
1466 | |
1467 template | |
1468 < | |
1469 std::uint8_t precision, | |
1470 typename MinorUnit, | |
1471 typename UnitStorage, | |
1472 typename FloatingPoint | |
1473 > | |
1474 FloatingPoint pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::maximum( | |
1475 const pecunia::currency::Codes& code | |
1476 ) | |
1477 { | |
1478 return pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>{ | |
1479 }.maximumAmountValue() / static_cast<FloatingPoint>( | |
1480 minorUnitPrecisionRatio(code, precision) | |
1481 ); | |
1482 } | |
1483 | |
1484 template | |
1485 < | |
1486 std::uint8_t precision, | |
1487 typename MinorUnit, | |
1488 typename UnitStorage, | |
1489 typename FloatingPoint | |
1490 > | |
1491 FloatingPoint pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>::minimum( | |
1492 const pecunia::currency::Codes& code | |
1493 ) | |
1494 { | |
1495 return pecunia::currency::Money<precision, MinorUnit, UnitStorage, FloatingPoint>{ | |
1496 }.minimumAmountValue() / static_cast<FloatingPoint>( | |
1497 minorUnitPrecisionRatio(code, precision) | |
1498 ); | |
1499 } | |
1500 | |
1501 #ifndef DOXYGEN_SHOULD_SKIP_THIS | |
1502 template<std::uint8_t p, typename MU, typename US, typename FP> | |
1503 pecunia::currency::Money<p, MU, US, FP> pecunia::currency::operator*(const FP lhs, const Money<p, MU, US, FP>& rhs) | |
1504 { | |
1505 static_assert(std::is_unsigned<MU>::value, "The MU must be unsigned."); | |
1506 static_assert(std::is_integral<MU>::value, "The MU must be an integer."); | |
1507 static_assert(std::is_floating_point<FP>::value, "The FP is not a floating-point type."); | |
1508 return rhs * lhs; | |
1509 } | |
1510 | |
1511 template<std::uint8_t p, typename MU, typename US, typename FP> | |
1512 std::ostream& pecunia::currency::operator<<(std::ostream& stream, const Money<p, MU, US, FP>& m) | |
1513 { | |
1514 static_assert(std::is_unsigned<MU>::value, "The MU must be unsigned."); | |
1515 static_assert(std::is_integral<MU>::value, "The MU must be an integer."); | |
1516 static_assert(std::is_floating_point<FP>::value, "The FP is not a floating-point type."); | |
1517 | |
1518 const bool isSymbolUsed{stream.iword(pecunia::currency::useSymbolIndex()) == 1}; | |
1519 const std::string currencyMarker{ | |
1520 isSymbolUsed ? toSymbol(m.iso4217Code_) : toIso4217(m.iso4217Code_) | |
1521 }; | |
1522 const bool isCountryLeading{stream.iword(pecunia::currency::countryLeadingIndex()) == 1}; | |
1523 const bool isSpaced{stream.iword(pecunia::currency::spacedIndex()) == 1}; | |
1524 const FP value{m}; | |
1525 stream << std::setprecision(minorUnitDigits(m.iso4217Code_) + p) << std::fixed | |
1526 << (isCountryLeading ? currencyMarker : "") | |
1527 << (isSpaced && isCountryLeading ? " " : "") | |
1528 << value | |
1529 << (isSpaced && ! isCountryLeading ? " " : "") | |
1530 << ( ! isCountryLeading ? currencyMarker : ""); | |
1531 return stream; | |
1532 } | |
1533 | |
1534 template<std::uint8_t p, typename MU, typename US, typename FP> | |
1535 std::istream& pecunia::currency::operator>>(std::istream& stream, Money<p, MU, US, FP>& m) | |
1536 { | |
1537 static_assert(std::is_unsigned<MU>::value, "The MU must be unsigned."); | |
1538 static_assert(std::is_integral<MU>::value, "The MU must be an integer."); | |
1539 static_assert(std::is_floating_point<FP>::value, "The FP is not a floating-point type."); | |
1540 | |
1541 const bool isCountryLeading{stream.iword(pecunia::currency::countryLeadingIndex()) == 1}; | |
1542 long double rawAmount; | |
1543 std::string rawCode; | |
1544 | |
1545 if (isCountryLeading) | |
1546 stream >> rawCode >> std::get_money(rawAmount); | |
1547 else | |
1548 stream >> std::get_money(rawAmount) >> rawCode; | |
1549 | |
1550 if ( ! stream) | |
1551 throw std::runtime_error{"Failed to read valid input for the monetary amount and/or code."}; | |
1552 | |
1553 m.iso4217Code_ = toCode(rawCode); | |
1554 const FP amount{ | |
1555 static_cast<FP>(rawAmount / pow(10, pecunia::currency::minorUnitDigits(m.iso4217Code_))) | |
1556 }; | |
1557 m = Money<p, MU, US, FP>{amount, m.iso4217Code_}; | |
1558 return stream; | |
1559 } | |
1560 #endif | |
1561 | |
1562 template<std::uint8_t p, typename MU, typename US, typename FP> | |
1563 std::string pecunia::currency::to_string(const Money<p, MU, US, FP>& m) | |
1564 { | |
1565 static_assert(std::is_unsigned<MU>::value, "The MU must be unsigned."); | |
1566 static_assert(std::is_integral<MU>::value, "The MU must be an integer."); | |
1567 static_assert(std::is_floating_point<FP>::value, "The FP is not a floating-point type."); | |
1568 | |
1569 std::stringstream ss; | |
1570 ss << pecunia::currency::spaced << pecunia::currency::trail << m; | |
1571 return ss.str(); | |
1572 } | |
1573 | |
1574 #endif |