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