-
Notifications
You must be signed in to change notification settings - Fork 5
/
doc.go
163 lines (127 loc) · 6.75 KB
/
doc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/*
Package money implements immutable amounts and exchange rates.
# Representation
[Currency] is represented as an integer index into an in-memory array that
stores properties defined by [ISO 4217]:
- Code: a three-letter alphabetic code.
- Num: a three-digit numeric code.
- Scale: a non-negative integer indicating the number of digits after
the decimal point needed to represent minor units of the currency.
The currently supported currencies use scales of 0, 2, or 3:
- A scale of 0 indicates currencies without minor units.
For example, the [Japanese Yen] does not have minor units.
- A scale of 2 indicates currencies that use 2 digits to represent their
minor units.
For example, the [US Dollar] represents its minor unit, 1 cent,
as 0.01 dollars.
- A scale of 3 indicates currencies with 3 digits in their minor units.
For instance, the minor unit of the [Omani Rial], 1 baisa, is represented
as 0.001 rials.
[Amount] is a struct with two fields:
- Currency: a [Currency] in which the amount is denominated.
- Amount: a [decimal.Decimal] representing the numeric value of the amount.
[ExchangeRate] is a struct with three fields:
- Base: a [Currency] being exchanged.
- Quote: a [Currency] being obtained in exchange for the base currency.
- Rate: a positive [decimal.Decimal] representing how many units of the quote
currency are needed to exchange for 1 unit of the base currency.
# Constraints
The range of an amount is determined by the scale of its currency.
Similarly, the range of an exchange rate is determined by the scale of its quote
currency.
Here are the ranges for scales 0, 2, and 3:
| Example | Scale | Minimum | Maximum |
| ------------ | ----- | ------------------------------------ | ----------------------------------- |
| Japanese Yen | 0 | -9,999,999,999,999,999,999 | 9,999,999,999,999,999,999 |
| US Dollar | 2 | -99,999,999,999,999,999.99 | 99,999,999,999,999,999.99 |
| Omani Rial | 3 | -9,999,999,999,999,999.999 | 9,999,999,999,999,999.999 |
Subnormal numbers are not supported by the underlying [decimal.Decimal] type.
Consequently, amounts and exchange rates between -0.00000000000000000005 and
0.00000000000000000005 inclusive are rounded to 0.
# Conversions
The package provides methods for converting:
- from/to string:
[ParseAmount], [Amount.String], [Amount.Format],
[ParseExchRate], [ExchangeRate.String], [ExchangeRate.Format].
- from/to float64:
[NewAmountFromFloat64], [Amount.Float64],
[NewExchRateFromFloat64], [ExchangeRate.Float64].
- from/to int64:
[NewAmount], [NewAmountFromInt64], [Amount.Int64],
[NewAmountFromMinorUnits], [Amount.MinorUnits],
[NewExchRate], [NewExchRateFromInt64], [ExchangeRate.Int64].
- from/to decimal:
[NewAmountFromDecimal], [Amount.Decimal],
[NewExchRateFromDecimal], [ExchangeRate.Decimal].
See the documentation for each method for more details.
# Operations
Each arithmetic operation is carried out in two steps:
1. The operation is initially performed using uint64 arithmetic.
If no overflow occurs, the exact result is immediately returned.
If an overflow does occur, the operation proceeds to step 2.
2. The operation is repeated with increased precision using [big.Int] arithmetic.
The result is then rounded to 19 digits.
If no significant digits are lost during rounding, the inexact result is returned.
If any significant digit is lost, an overflow error is returned.
Step 1 was introduced to improve performance by avoiding heap allocation
for [big.Int] and the complexities associated with [big.Int] arithmetic.
It is expected that, in transactional financial systems, the majority of
arithmetic operations will successfully compute an exact result during step 1.
The following rules are used to determine the significance of digits during step 2:
- [Amount.Add], [Amount.Sub], [Amount.SubAbs], [Amount.Mul], [Amount.FMA],
[Amount.Quo], [Amount.QuoRem], [ExchangeRate.Conv], [ExchangeRate.Mul],
[ExchangeRate.Inv]:
All digits in the integer part are significant.
In the fractional part, digits are significant up to the scale of
the currency.
- [Amount.Rat]:
All digits in the integer part are significant, while digits in the
fractional part are considered insignificant.
# Rounding
Implicit rounding is applied when a result exceeds 19 digits.
In such cases, the result is rounded to 19 digits using half-to-even rounding.
This method ensures that rounding errors are evenly distributed between rounding up
and rounding down.
For all arithmetic operations, the result is the one that would be obtained by
computing the exact mathematical result with infinite precision and then
rounding it to 19 digits.
In addition to implicit rounding, the package provides several methods for
explicit rounding:
- half-to-even rounding:
[Amount.Round], [Amount.RoundToCurr], [Amount.Quantize], [Amount.Rescale],
[ExchangeRate.Round], [ExchangeRate.Quantize], [ExchangeRate.Rescale].
- rounding towards positive infinity:
[Amount.Ceil], [Amount.CeilToCurr], [ExchangeRate.Ceil].
- rounding towards negative infinity:
[Amount.Floor], [Amount.FloorToCurr], [ExchangeRate.Floor].
- rounding towards zero:
[Amount.Trunc], [Amount.TruncToCurr], [ExchangeRate.Trunc].
See the documentation for each method for more details.
# Errors
All methods are panic-free and pure.
Errors are returned in the following cases:
- Currency Mismatch.
All arithmetic operations, except for [Amount.Rat], return an error if
the operands are denominated in different currencies.
- Division by Zero.
Unlike the standard library, [Amount.Quo], [Amount.QuoRem], [Amount.Rat],
and [ExchangeRate.Inv] do not panic when dividing by 0.
Instead, they return an error.
- Overflow.
Unlike standard integers, there is no "wrap around" for amounts at certain sizes.
Arithmetic operations return an error for out-of-range values.
- Underflow.
All arithmetic operations, except for [ExchangeRate.Inv] and [ExchangeRate.Mul],
do not return an error in case of underflow.
If the result is an amount between -0.00000000000000000005
and 0.00000000000000000005 inclusive, it is rounded to 0.
[ExchangeRate.Inv] and [ExchangeRate.Mul] return an error in cases of underflow,
as the result of these operations is an exchange rate, and exchange rates
cannot be 0.
[Japanese Yen]: https://en.wikipedia.org/wiki/Japanese_yen
[US Dollar]: https://en.wikipedia.org/wiki/United_States_dollar
[Omani Rial]: https://en.wikipedia.org/wiki/Omani_rial
[ISO 4217]: https://en.wikipedia.org/wiki/ISO_4217
[big.Int]: https://pkg.go.dev/math/big#Int
*/
package money