iso-price 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/contract/index.d.ts +25 -0
- package/dist/contract/index.js +60 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/domain.objects/IsoCurrency.d.ts +63 -0
- package/dist/domain.objects/IsoCurrency.js +70 -0
- package/dist/domain.objects/IsoCurrency.js.map +1 -0
- package/dist/domain.objects/IsoPrice.d.ts +16 -0
- package/dist/domain.objects/IsoPrice.js +3 -0
- package/dist/domain.objects/IsoPrice.js.map +1 -0
- package/dist/domain.objects/IsoPriceExponent.d.ts +28 -0
- package/dist/domain.objects/IsoPriceExponent.js +33 -0
- package/dist/domain.objects/IsoPriceExponent.js.map +1 -0
- package/dist/domain.objects/IsoPriceHuman.d.ts +19 -0
- package/dist/domain.objects/IsoPriceHuman.js +3 -0
- package/dist/domain.objects/IsoPriceHuman.js.map +1 -0
- package/dist/domain.objects/IsoPriceRoundMode.d.ts +23 -0
- package/dist/domain.objects/IsoPriceRoundMode.js +28 -0
- package/dist/domain.objects/IsoPriceRoundMode.js.map +1 -0
- package/dist/domain.objects/IsoPriceShape.d.ts +30 -0
- package/dist/domain.objects/IsoPriceShape.js +3 -0
- package/dist/domain.objects/IsoPriceShape.js.map +1 -0
- package/dist/domain.objects/IsoPriceWords.d.ts +20 -0
- package/dist/domain.objects/IsoPriceWords.js +3 -0
- package/dist/domain.objects/IsoPriceWords.js.map +1 -0
- package/dist/domain.operations/arithmetic/allocatePrice.d.ts +48 -0
- package/dist/domain.operations/arithmetic/allocatePrice.js +167 -0
- package/dist/domain.operations/arithmetic/allocatePrice.js.map +1 -0
- package/dist/domain.operations/arithmetic/dividePrice.d.ts +40 -0
- package/dist/domain.operations/arithmetic/dividePrice.js +127 -0
- package/dist/domain.operations/arithmetic/dividePrice.js.map +1 -0
- package/dist/domain.operations/arithmetic/multiplyPrice.d.ts +38 -0
- package/dist/domain.operations/arithmetic/multiplyPrice.js +89 -0
- package/dist/domain.operations/arithmetic/multiplyPrice.js.map +1 -0
- package/dist/domain.operations/arithmetic/subPrices.d.ts +28 -0
- package/dist/domain.operations/arithmetic/subPrices.js +62 -0
- package/dist/domain.operations/arithmetic/subPrices.js.map +1 -0
- package/dist/domain.operations/arithmetic/sumPrices.d.ts +44 -0
- package/dist/domain.operations/arithmetic/sumPrices.js +88 -0
- package/dist/domain.operations/arithmetic/sumPrices.js.map +1 -0
- package/dist/domain.operations/cast/asIsoPrice.d.ts +35 -0
- package/dist/domain.operations/cast/asIsoPrice.js +117 -0
- package/dist/domain.operations/cast/asIsoPrice.js.map +1 -0
- package/dist/domain.operations/cast/asIsoPriceHuman.d.ts +25 -0
- package/dist/domain.operations/cast/asIsoPriceHuman.js +106 -0
- package/dist/domain.operations/cast/asIsoPriceHuman.js.map +1 -0
- package/dist/domain.operations/cast/asIsoPriceShape.d.ts +25 -0
- package/dist/domain.operations/cast/asIsoPriceShape.js +164 -0
- package/dist/domain.operations/cast/asIsoPriceShape.js.map +1 -0
- package/dist/domain.operations/cast/asIsoPriceWords.d.ts +25 -0
- package/dist/domain.operations/cast/asIsoPriceWords.js +103 -0
- package/dist/domain.operations/cast/asIsoPriceWords.js.map +1 -0
- package/dist/domain.operations/guard/isIsoPrice.d.ts +18 -0
- package/dist/domain.operations/guard/isIsoPrice.js +29 -0
- package/dist/domain.operations/guard/isIsoPrice.js.map +1 -0
- package/dist/domain.operations/guard/isIsoPriceHuman.d.ts +24 -0
- package/dist/domain.operations/guard/isIsoPriceHuman.js +76 -0
- package/dist/domain.operations/guard/isIsoPriceHuman.js.map +1 -0
- package/dist/domain.operations/guard/isIsoPriceShape.d.ts +29 -0
- package/dist/domain.operations/guard/isIsoPriceShape.js +50 -0
- package/dist/domain.operations/guard/isIsoPriceShape.js.map +1 -0
- package/dist/domain.operations/guard/isIsoPriceWords.d.ts +24 -0
- package/dist/domain.operations/guard/isIsoPriceWords.js +48 -0
- package/dist/domain.operations/guard/isIsoPriceWords.js.map +1 -0
- package/dist/domain.operations/precision/getIsoPriceExponentByCurrency.d.ts +15 -0
- package/dist/domain.operations/precision/getIsoPriceExponentByCurrency.js +49 -0
- package/dist/domain.operations/precision/getIsoPriceExponentByCurrency.js.map +1 -0
- package/dist/domain.operations/precision/roundPrice.d.ts +25 -0
- package/dist/domain.operations/precision/roundPrice.js +24 -0
- package/dist/domain.operations/precision/roundPrice.js.map +1 -0
- package/dist/domain.operations/precision/setPricePrecision.d.ts +29 -0
- package/dist/domain.operations/precision/setPricePrecision.js +119 -0
- package/dist/domain.operations/precision/setPricePrecision.js.map +1 -0
- package/dist/domain.operations/statistics/calcPriceAvg.d.ts +21 -0
- package/dist/domain.operations/statistics/calcPriceAvg.js +92 -0
- package/dist/domain.operations/statistics/calcPriceAvg.js.map +1 -0
- package/dist/domain.operations/statistics/calcPriceStdev.d.ts +23 -0
- package/dist/domain.operations/statistics/calcPriceStdev.js +137 -0
- package/dist/domain.operations/statistics/calcPriceStdev.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/license.md +21 -0
- package/package.json +102 -0
- package/readme.md +373 -0
package/readme.md
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
# iso-price
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
a pit of success for prices. iso 4217 currencies. si metric precision. ecmascript numeric separators.
|
|
7
|
+
|
|
8
|
+
## why
|
|
9
|
+
|
|
10
|
+
money math is deceptively hard:
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
0.1 + 0.2 === 0.30000000000000004 // float math
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
most solutions add complexity: decimal libraries, bigint wrappers, money objects. iso-price takes the opposite approach — make the correct choice simple.
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { sumPrices } from 'iso-price';
|
|
20
|
+
|
|
21
|
+
sumPrices('USD 0.10', 'USD 0.20'); // => 'USD 0.30'
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
that's it. words in, words out. the precision, currency validation, and bigint arithmetic happen automatically.
|
|
25
|
+
|
|
26
|
+
## design
|
|
27
|
+
|
|
28
|
+
**any input, safe output.**
|
|
29
|
+
|
|
30
|
+
throw any format at it — `'$50.37'`, `'USD 50.37'`, `{ amount: 5037, currency: 'USD' }`, `{ amount: 5037n, currency: 'USD' }` — and get back `IsoPriceWords`: a string that is serializable, lossless, readable, composable, and standards-conformant.
|
|
31
|
+
|
|
32
|
+
when you need structured access for schema persistence or performance optimization:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
asIsoPriceShape('USD 50.37'); // => { amount: 5037n, currency: 'USD' }
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
when you need a ux-friendly format for display:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
asIsoPriceHuman('USD 50.37'); // => '$50.37'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
by default, you get the best of all worlds.
|
|
45
|
+
|
|
46
|
+
**thin contract, deep behavior.**
|
|
47
|
+
|
|
48
|
+
the surface is simple: prices are strictly structured strings formatted like `'USD 50.37'`. under the hood: sub-cent precision, currency exponents, lossless bigint arithmetic, automatic precision normalization.
|
|
49
|
+
|
|
50
|
+
**explicit, not magic.**
|
|
51
|
+
|
|
52
|
+
precision is visible in the string. `'USD 50.370_000'` shows micro-dollar precision. `'USD 0.000_000_250'` shows nano-dollar precision. no hidden state, no surprises.
|
|
53
|
+
|
|
54
|
+
**standards-based.**
|
|
55
|
+
|
|
56
|
+
- **iso 4217** — currency codes and exponents (USD=2, JPY=0, BHD=3)
|
|
57
|
+
- **si metric prefixes** — centi, milli, micro, nano, pico
|
|
58
|
+
- **ecmascript numeric separators** — underscores for readability
|
|
59
|
+
|
|
60
|
+
## install
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
npm install iso-price
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## usage
|
|
67
|
+
|
|
68
|
+
### the basics
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import { asIsoPrice, sumPrices, multiplyPrice } from 'iso-price';
|
|
72
|
+
|
|
73
|
+
// parse any format
|
|
74
|
+
const price = asIsoPrice('$50.37'); // => 'USD 50.37'
|
|
75
|
+
const price = asIsoPrice('EUR 100.00'); // => 'EUR 100.00'
|
|
76
|
+
|
|
77
|
+
// arithmetic just works, regardless of input format
|
|
78
|
+
sumPrices('$10', 'USD 20.00'); // => 'USD 30.00'
|
|
79
|
+
multiplyPrice({ of: '$100', by: 1.08 }); // => 'USD 108.00'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### sub-cent precision
|
|
83
|
+
|
|
84
|
+
llm token costs, serverless invocations, crypto — sometimes you need more than cents:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { dividePrice, sumPrices } from 'iso-price';
|
|
88
|
+
|
|
89
|
+
// $0.25 per million tokens
|
|
90
|
+
const perToken = dividePrice({ of: '$0.25', by: 1_000_000 });
|
|
91
|
+
// => 'USD 0.000_000_250'
|
|
92
|
+
|
|
93
|
+
// track micro-costs, sum to invoice
|
|
94
|
+
const costs = ['USD 0.011_845_500', 'USD 47.370_001_970'];
|
|
95
|
+
sumPrices(costs); // => 'USD 47.381_847_470'
|
|
96
|
+
|
|
97
|
+
// cross-precision arithmetic auto-resolves to most granular
|
|
98
|
+
sumPrices('USD 50.00', 'USD 0.000_005'); // => 'USD 50.000_005'
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
precision scales automatically. no configuration needed.
|
|
102
|
+
|
|
103
|
+
### three formats
|
|
104
|
+
|
|
105
|
+
| format | example | use |
|
|
106
|
+
| --------------- | ------------------------------------ | ------------------------ |
|
|
107
|
+
| `IsoPriceWords` | `'USD 50.37'` | storage, logs, api, json |
|
|
108
|
+
| `IsoPriceShape` | `{ amount: 5037n, currency: 'USD' }` | computation |
|
|
109
|
+
| `IsoPriceHuman` | `'$50.37'` | display |
|
|
110
|
+
|
|
111
|
+
all operations accept any format. all return `IsoPriceWords` by default.
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { asIsoPriceShape, asIsoPriceHuman } from 'iso-price';
|
|
115
|
+
|
|
116
|
+
// need structured access for stripe or persistance?
|
|
117
|
+
asIsoPriceShape('USD 50.37'); // => { amount: 5037n, currency: 'USD' }
|
|
118
|
+
|
|
119
|
+
// need display format?
|
|
120
|
+
asIsoPriceHuman('USD 50.37'); // => '$50.37'
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### allocation without loss
|
|
124
|
+
|
|
125
|
+
money splits create remainders. iso-price handles them:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { allocatePrice } from 'iso-price';
|
|
129
|
+
|
|
130
|
+
allocatePrice({ of: 'USD 10.00', into: { parts: 3 }, remainder: 'first' });
|
|
131
|
+
// => ['USD 3.34', 'USD 3.33', 'USD 3.33']
|
|
132
|
+
// sum: exactly $10.00
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## api
|
|
136
|
+
|
|
137
|
+
### cast
|
|
138
|
+
|
|
139
|
+
- `asIsoPrice(input)` — normalize to words
|
|
140
|
+
- `asIsoPriceWords(input)` — convert to words
|
|
141
|
+
- `asIsoPriceShape(input)` — convert to shape
|
|
142
|
+
- `asIsoPriceHuman(input)` — convert to display
|
|
143
|
+
|
|
144
|
+
### arithmetic
|
|
145
|
+
|
|
146
|
+
- `sumPrices(...prices)` / `priceSum` / `addPrices` / `priceAdd`
|
|
147
|
+
- `subPrices(a, b)` / `priceSub`
|
|
148
|
+
- `multiplyPrice({ of, by })` / `priceMultiply`
|
|
149
|
+
- `dividePrice({ of, by })` / `priceDivide`
|
|
150
|
+
- `allocatePrice({ of, into, remainder })` / `priceAllocate`
|
|
151
|
+
|
|
152
|
+
### precision
|
|
153
|
+
|
|
154
|
+
- `setPricePrecision({ of, to }, options?)`
|
|
155
|
+
- `roundPrice({ of }, options?)`
|
|
156
|
+
- `getIsoPriceExponentByCurrency(currency)`
|
|
157
|
+
|
|
158
|
+
### statistics
|
|
159
|
+
|
|
160
|
+
- `calcPriceAvg(prices)`
|
|
161
|
+
- `calcPriceStdev(prices)`
|
|
162
|
+
|
|
163
|
+
### guards
|
|
164
|
+
|
|
165
|
+
- `isIsoPrice(input)` / `.assure(input)`
|
|
166
|
+
- `isIsoPriceWords(input)` / `.assure(input)`
|
|
167
|
+
- `isIsoPriceShape(input)` / `.assure(input)`
|
|
168
|
+
- `isIsoPriceHuman(input)` / `.assure(input)`
|
|
169
|
+
|
|
170
|
+
### types
|
|
171
|
+
|
|
172
|
+
- `IsoPrice<TCurrency>` — union of all formats
|
|
173
|
+
- `IsoPriceWords<TCurrency>` — branded string
|
|
174
|
+
- `IsoPriceShape<TCurrency>` — bigint object
|
|
175
|
+
- `IsoPriceHuman` — display string
|
|
176
|
+
- `IsoPriceExponent` — precision enum
|
|
177
|
+
- `IsoPriceRoundMode` — round mode enum
|
|
178
|
+
- `IsoCurrency` — top 25 currencies enum
|
|
179
|
+
|
|
180
|
+
## currency exponents
|
|
181
|
+
|
|
182
|
+
iso 4217 defines the standard precision (exponent) for each currency. iso-price applies these automatically:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
asIsoPrice('$50.37'); // => 'USD 50.37' (2 decimals — cents)
|
|
186
|
+
asIsoPrice('¥1000'); // => 'JPY 1000' (0 decimals — no minor unit)
|
|
187
|
+
asIsoPrice('BHD 1.234'); // => 'BHD 1.234' (3 decimals — fils)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
| currency | exponent | minor unit | example |
|
|
191
|
+
| ------------- | -------- | ---------- | ------------- |
|
|
192
|
+
| USD, EUR, GBP | 2 | cents | `'USD 50.37'` |
|
|
193
|
+
| JPY, KRW, VND | 0 | none | `'JPY 1000'` |
|
|
194
|
+
| BHD, KWD, OMR | 3 | fils/baisa | `'BHD 1.234'` |
|
|
195
|
+
|
|
196
|
+
when you need more precision than the standard (llm tokens, serverless), iso-price extends with si metric prefixes:
|
|
197
|
+
|
|
198
|
+
## exponents
|
|
199
|
+
|
|
200
|
+
si metric prefixes for explicit precision:
|
|
201
|
+
|
|
202
|
+
| exponent | factor | iso 4217 | examples |
|
|
203
|
+
| -------------- | ------ | ---------- | ---------- |
|
|
204
|
+
| `whole.x10^0` | 10⁰ | 0 decimals | jpy, krw |
|
|
205
|
+
| `centi.x10^-2` | 10⁻² | 2 decimals | usd, eur |
|
|
206
|
+
| `milli.x10^-3` | 10⁻³ | 3 decimals | bhd, kwd |
|
|
207
|
+
| `micro.x10^-6` | 10⁻⁶ | — | llm tokens |
|
|
208
|
+
| `nano.x10^-9` | 10⁻⁹ | — | serverless |
|
|
209
|
+
| `pico.x10^-12` | 10⁻¹² | — | extreme |
|
|
210
|
+
|
|
211
|
+
## real-world examples
|
|
212
|
+
|
|
213
|
+
### e-commerce invoice
|
|
214
|
+
|
|
215
|
+
standard cents precision — the common case:
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
const lineItems = ['USD 29.99', 'USD 14.50', 'USD 5.99'];
|
|
219
|
+
const subtotal = sumPrices(lineItems); // => 'USD 50.48'
|
|
220
|
+
const withTax = multiplyPrice({ of: subtotal, by: 1.08 }); // => 'USD 54.52'
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### llm api cost aggregation
|
|
224
|
+
|
|
225
|
+
nano-dollar precision for per-token costs:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
// claude haiku: $0.25 per million input tokens
|
|
229
|
+
const costPerToken = dividePrice({ of: 'USD 0.25', by: 1_000_000 });
|
|
230
|
+
// => 'USD 0.000_000_250'
|
|
231
|
+
|
|
232
|
+
// accumulate usage across requests
|
|
233
|
+
const costsAccumulated = [
|
|
234
|
+
'USD 0.000_047_250', // 189 tokens
|
|
235
|
+
'USD 0.000_125_000', // 500 tokens
|
|
236
|
+
'USD 0.002_847_500', // 11,390 tokens
|
|
237
|
+
];
|
|
238
|
+
|
|
239
|
+
// sum micro-costs into invoiceable amount
|
|
240
|
+
const costTotal = sumPrices(costsAccumulated); // => 'USD 0.003_019_750'
|
|
241
|
+
|
|
242
|
+
// combine with standard e-commerce charges
|
|
243
|
+
const platformFee = 'USD 9.99';
|
|
244
|
+
const invoice = sumPrices(platformFee, costTotal);
|
|
245
|
+
// => 'USD 9.993_019_750'
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### bill allocation
|
|
249
|
+
|
|
250
|
+
allocate without remainder loss:
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
// split a $100 dinner bill 3 ways
|
|
254
|
+
allocatePrice({ of: 'USD 100.00', into: { parts: 3 }, remainder: 'first' });
|
|
255
|
+
// => ['USD 33.34', 'USD 33.33', 'USD 33.33']
|
|
256
|
+
// sum: exactly $100.00
|
|
257
|
+
|
|
258
|
+
// split by ratio (60/40)
|
|
259
|
+
allocatePrice({ of: 'USD 100.00', into: { ratios: [6, 4] }, remainder: 'first' });
|
|
260
|
+
// => ['USD 60.00', 'USD 40.00']
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## currency symbols
|
|
264
|
+
|
|
265
|
+
currency symbols are lossy — `$` could be USD, CAD, AUD, or 20+ other currencies. iso-price defaults to the most common:
|
|
266
|
+
|
|
267
|
+
| symbol | default | also used by |
|
|
268
|
+
| ------ | ------- | --------------------------------- |
|
|
269
|
+
| `$` | USD | CAD, AUD, NZD, MXN, SGD, HKD, ... |
|
|
270
|
+
| `€` | EUR | (unique) |
|
|
271
|
+
| `£` | GBP | EGP, LBP, SYP, ... |
|
|
272
|
+
| `¥` | JPY | CNY |
|
|
273
|
+
|
|
274
|
+
override when needed:
|
|
275
|
+
|
|
276
|
+
```ts
|
|
277
|
+
asIsoPrice('$50.37'); // => 'USD 50.37'
|
|
278
|
+
asIsoPrice('$50.37', { currency: 'CAD' }); // => 'CAD 50.37'
|
|
279
|
+
asIsoPrice('$50.37', { currency: 'AUD' }); // => 'AUD 50.37'
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
for unambiguous storage and transmission, always use `IsoPriceWords` format (`'USD 50.37'`). use `IsoPriceHuman` (`'$50.37'`) only for display.
|
|
283
|
+
|
|
284
|
+
## serialization
|
|
285
|
+
|
|
286
|
+
`IsoPriceWords` is a string — it serializes trivially:
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
const price = sumPrices('USD 10.00', 'USD 20.00'); // => 'USD 30.00'
|
|
290
|
+
|
|
291
|
+
JSON.stringify({ total: price });
|
|
292
|
+
// => '{"total":"USD 30.00"}'
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
`IsoPriceShape` uses bigint, which cannot be serialized directly:
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
JSON.stringify({ amount: 5037n });
|
|
299
|
+
// => TypeError: Do not know how to serialize a BigInt
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
this is intentional — it pushes you toward `IsoPriceWords` for persistence, which is portable, human-readable, and lossless.
|
|
303
|
+
|
|
304
|
+
## persistence
|
|
305
|
+
|
|
306
|
+
| database | recommendation | storage |
|
|
307
|
+
| --------------------------- | ----------------------- | ------------------------------ |
|
|
308
|
+
| dynamodb, mongodb, s3, json | `IsoPriceWords` | `"USD 50.37"` |
|
|
309
|
+
| postgres, sqlite | `iso_price` extension | native arithmetic + comparison |
|
|
310
|
+
| mysql, mariadb | see persistence catalog | many considerations |
|
|
311
|
+
|
|
312
|
+
**nosql / json** — store as `IsoPriceWords` string. these databases don't support numeric price comparisons anyway, so use the portable, human-readable format:
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
await dynamodb.put({ total: 'USD 50.37' });
|
|
316
|
+
const price = asIsoPrice(item.total); // => 'USD 50.37'
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
**postgres / sqlite** — use the `iso_price` extension for native operators:
|
|
320
|
+
|
|
321
|
+
```sql
|
|
322
|
+
SELECT * FROM items WHERE price > 'USD 10.00' ORDER BY price;
|
|
323
|
+
SELECT sum(price) FROM line_items; -- auto-normalizes exponents
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**other sql** (mysql, mariadb, etc) — these databases don't support extensions, so price comparisons across currencies and precisions require careful consideration. see the [persistence catalog](./.agent/repo=.this/role=dbadmin/briefs/per001.persistence._.[catalog].md) for patterns.
|
|
327
|
+
|
|
328
|
+
## round modes
|
|
329
|
+
|
|
330
|
+
when precision decreases, a round mode is required:
|
|
331
|
+
|
|
332
|
+
```ts
|
|
333
|
+
import { setPricePrecision, roundPrice } from 'iso-price';
|
|
334
|
+
|
|
335
|
+
// default: half-up (standard round behavior)
|
|
336
|
+
setPricePrecision({ of: 'USD 5.555', to: 'centi.x10^-2' });
|
|
337
|
+
// => 'USD 5.56'
|
|
338
|
+
|
|
339
|
+
// explicit round modes
|
|
340
|
+
setPricePrecision({ of: 'USD 5.555', to: 'centi.x10^-2' }, { round: 'floor' });
|
|
341
|
+
// => 'USD 5.55'
|
|
342
|
+
|
|
343
|
+
setPricePrecision({ of: 'USD 5.555', to: 'centi.x10^-2' }, { round: 'ceil' });
|
|
344
|
+
// => 'USD 5.56'
|
|
345
|
+
|
|
346
|
+
// round to currency's standard precision
|
|
347
|
+
roundPrice({ of: 'USD 5.555_555' }); // => 'USD 5.56'
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
| mode | behavior | 5.555 → cents |
|
|
351
|
+
| ----------- | ------------------------------------- | ------------- |
|
|
352
|
+
| `half-up` | round half toward +∞ (default) | 5.56 |
|
|
353
|
+
| `half-down` | round half toward 0 | 5.55 |
|
|
354
|
+
| `half-even` | round half to nearest even (banker's) | 5.56 |
|
|
355
|
+
| `floor` | toward −∞ | 5.55 |
|
|
356
|
+
| `ceil` | toward +∞ | 5.56 |
|
|
357
|
+
| `trunc` | toward 0 | 5.55 |
|
|
358
|
+
|
|
359
|
+
## supported currencies
|
|
360
|
+
|
|
361
|
+
`IsoCurrency` includes the top 25 currencies by forex volume plus the 3-decimal currencies:
|
|
362
|
+
|
|
363
|
+
```ts
|
|
364
|
+
enum IsoCurrency {
|
|
365
|
+
// top by volume
|
|
366
|
+
USD, EUR, JPY, GBP, CNY, AUD, CAD, CHF, HKD, NZD,
|
|
367
|
+
SEK, KRW, SGD, NOK, MXN, INR, ZAR, BRL, DKK, PLN, THB,
|
|
368
|
+
// 3-decimal (fils/baisa)
|
|
369
|
+
BHD, KWD, OMR, TND,
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
custom currencies (BTC, ETH, or any 3-letter code) are supported — they default to 2-decimal precision unless explicitly specified.
|