diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,9 +1,39 @@
+# Version 0.8
+
+* _COMPILER ASSISTED BREAKING CHANGE_. Introduced `DecimalConf`, which all
+  of the `xxxToDecimal` and `xxxFromDecimal` are now using. This concentrates
+  all decimal rendering and parsing settings in a single place, and more
+  importantly, it makes all of the `xxxToDecimal` return a `Text` value
+  directly, rather than a `Maybe Text`. In turn, `Separators` has been
+  introduced for configuring decimal and thousands separators. See issue #30.
+
+* _COMPILER ASSISTED BREAKING CHANGE_. The `Scale` type family was renamed to
+  `UnitScale`.
+
+* _COMPILER ASSISTED BREAKING CHANGE_. Previous instances of the `Scale` type
+  family where the `currency` and the `unit` were the same (e.g., `Scale "USD"
+  "USD"`), intended to convey a canonical scale for the currency, are now
+  written as `CurrencyScale currency` (e.g., `CurrencyScale "USD"`).
+
+* _COMPILER ASSISTED BREAKING CHANGE_. A new datatype called `Scale` has been
+  introduced. This datatype replaces term-level uses of `Rational` to convey the
+  scale of a discrete monetary amount. The rationale for this, beyond improved
+  type errors, is that checking for the scale's validity can be done at a single
+  place (namely, the new `scaleFromRational`), rather than everywhere a
+  term-level scale was expected as input. See issue #43.
+
+* Added new approximation method `HalfEven`, also known as “Bankers rounding”.
+  See issue #42.
+
+* New currencies: VES (see issue #33), UYW.
+
+
 # Version 0.7.1
 
 * Fixed compilation with GHC 8.6.
 
 * Fixed an issue when rendering decimal values smaller than the smallest
-  possible fractional part. (Issue #39)
+  possible fractional part. See issue #39.
 
 
 # Version 0.7
diff --git a/safe-money.cabal b/safe-money.cabal
--- a/safe-money.cabal
+++ b/safe-money.cabal
@@ -1,5 +1,5 @@
 name: safe-money
-version: 0.7.1
+version: 0.8
 license: BSD3
 license-file: LICENSE
 copyright: Copyright (c) Renzo Carbonara 2016-2018
diff --git a/src/Money.hs b/src/Money.hs
--- a/src/Money.hs
+++ b/src/Money.hs
@@ -11,845 +11,869 @@
 --
 -- Note: This module exports support for many well-known currencies
 -- out-of-the-box, but you are not limited to the currencies mentioned here. You
--- can simply create a new 'I.Scale' instance, and /voilà/. If you want to add a
--- new currency to the out-of-the-box offer, please request so in
--- https://github.com/k0001/safe-money/issues and the authors will see to it.
---
--- This module offers plenty of documentation, but for a deep explanation of
--- how all of the pieces fit together, please read
--- <https://ren.zone/articles/safe-money>. Notice, however, that this library
--- has changed a bit since that article was written. You can always see the
--- [change log](https://github.com/k0001/safe-money/blob/master/changelog.md) to
--- understand what has changed.
---
--- Also, keep in mind that useful instances for the many types defined by
--- @safe-money@ can be found in these other libraries:
---
--- * [safe-money-aeson](https://hackage.haskell.org/package/safe-money-aeson):
---   `FromJSON` and `ToJSON` instances (from the
---   [aeson](https://hackage.haskell.org/package/aeson) library).
---
--- * [safe-money-cereal](https://hackage.haskell.org/package/safe-money-cereal):
---   `Serialize` instances (from the
---   [cereal](https://hackage.haskell.org/package/cereal) library).
---
--- * [safe-money-serialise](https://hackage.haskell.org/package/safe-money-serialise):
---   `Serialise` instances (from the
---   [serialise](https://hackage.haskell.org/package/serialise) library).
---
--- * [safe-money-store](https://hackage.haskell.org/package/safe-money-store):
---   `Store` instances (from the
---   [store](https://hackage.haskell.org/package/store) library).
---
--- * [safe-money-xmlbf](https://hackage.haskell.org/package/safe-money-xmlbf):
---   `FromXml` and `ToXml` instances (from the
---   [xmlbf](https://hackage.haskell.org/package/xmlbf) library).
-module Money
- ( -- * Dense monetary values
-   I.Dense
- , I.denseCurrency
- , I.dense
- , I.dense'
- , I.denseFromDiscrete
- , I.denseFromDecimal
- , I.denseToDecimal
-   -- * Discrete monetary values
- , I.Discrete
- , I.Discrete'
- , I.discrete
- , I.discreteCurrency
- , I.discreteFromDense
- , I.discreteFromDecimal
- , I.discreteToDecimal
-   -- * Currency scales
- , I.Scale
- , I.GoodScale
- , I.ErrScaleNonCanonical
- , I.scale
-   -- * Currency exchange
- , I.ExchangeRate
- , I.exchangeRate
- , I.exchange
- , I.exchangeRateRecip
- , I.exchangeRateFromDecimal
- , I.exchangeRateToDecimal
- , I.exchangeRateToRational
-   -- * Serializable representations
- , I.SomeDense
- , I.toSomeDense
- , I.mkSomeDense
- , I.fromSomeDense
- , I.withSomeDense
- , I.someDenseCurrency
- , I.someDenseAmount
- , I.SomeDiscrete
- , I.toSomeDiscrete
- , I.mkSomeDiscrete
- , I.fromSomeDiscrete
- , I.withSomeDiscrete
- , I.someDiscreteCurrency
- , I.someDiscreteScale
- , I.someDiscreteAmount
- , I.SomeExchangeRate
- , I.toSomeExchangeRate
- , I.mkSomeExchangeRate
- , I.fromSomeExchangeRate
- , I.withSomeExchangeRate
- , I.someExchangeRateSrcCurrency
- , I.someExchangeRateDstCurrency
- , I.someExchangeRateRate
- -- * Misc
- , I.Approximation(Round, Floor, Ceiling, Truncate)
- ) where
-
-import qualified Money.Internal as I
-
---------------------------------------------------------------------------------
--- Currency scales
-
--- | United Arab Emirates dirham
-type instance I.Scale "AED" "AED" = '(100, 1)
-type instance I.Scale "AED" "dirham" = '(1, 1)
-type instance I.Scale "AED" "fils" = '(100, 1)
--- | Afghan afghani
-type instance I.Scale "AFN" "AFN" = '(100, 1)
-type instance I.Scale "AFN" "afghani" = '(1, 1)
-type instance I.Scale "AFN" "pul" = '(100, 1)
--- | Albanian lek
-type instance I.Scale "ALL" "ALL" = '(100, 1)
-type instance I.Scale "ALL" "lek" = '(1, 1)
-type instance I.Scale "ALL" "qindarke" = '(100, 1)
--- | Armenian dram
-type instance I.Scale "AMD" "AMD" = '(100, 1)
-type instance I.Scale "AMD" "dram" = '(1, 1)
-type instance I.Scale "AMD" "luma" = '(100, 1)
--- | Netherlands Antillean guilder
-type instance I.Scale "ANG" "ANG" = '(100, 1)
-type instance I.Scale "ANG" "guilder" = '(1, 1)
-type instance I.Scale "ANG" "cent" = '(100, 1)
--- | Angolan kwanza
-type instance I.Scale "AOA" "AOA" = '(100, 1)
-type instance I.Scale "AOA" "kwanza" = '(1, 1)
-type instance I.Scale "AOA" "centimo" = '(100, 1)
--- | Argentine peso
-type instance I.Scale "ARS" "ARS" = '(100, 1)
-type instance I.Scale "ARS" "peso" = '(1, 1)
-type instance I.Scale "ARS" "centavo" = '(100, 1)
--- | Australian dollar
-type instance I.Scale "AUD" "AUD" = '(100, 1)
-type instance I.Scale "AUD" "dollar" = '(1, 1)
-type instance I.Scale "AUD" "cent" = '(100, 1)
--- | Aruban florin
-type instance I.Scale "AWG" "AWG" = '(100, 1)
-type instance I.Scale "AWG" "florin" = '(1, 1)
-type instance I.Scale "AWG" "cent" = '(100, 1)
--- | Azerbaijani manat
-type instance I.Scale "AZN" "AZN" = '(100, 1)
-type instance I.Scale "AZN" "manat" = '(1, 1)
-type instance I.Scale "AZN" "qapik" = '(100, 1)
--- | Bosnia and Herzegovina convertible mark
-type instance I.Scale "BAM" "BAM" = '(100, 1)
-type instance I.Scale "BAM" "mark" = '(1, 1)
-type instance I.Scale "BAM" "fening" = '(100, 1)
--- | Barbadian dollar
-type instance I.Scale "BBD" "BBD" = '(100, 1)
-type instance I.Scale "BBD" "dollar" = '(1, 1)
-type instance I.Scale "BBD" "cent" = '(100, 1)
--- | Bangladeshi taka
-type instance I.Scale "BDT" "BDT" = '(100, 1)
-type instance I.Scale "BDT" "taka" = '(1, 1)
-type instance I.Scale "BDT" "paisa" = '(100, 1)
--- | Bulgarian lev
-type instance I.Scale "BGN" "BGN" = '(100, 1)
-type instance I.Scale "BGN" "lev" = '(1, 1)
-type instance I.Scale "BGN" "stotinka" = '(100, 1)
--- | Bahraini dinar
-type instance I.Scale "BHD" "BHD" = '(1000, 1)
-type instance I.Scale "BHD" "dinar" = '(1, 1)
-type instance I.Scale "BHD" "fils" = '(1000, 1)
--- | Burundi franc
-type instance I.Scale "BIF" "BIF" = '(100, 1)
-type instance I.Scale "BIF" "franc" = '(1, 1)
-type instance I.Scale "BIF" "centime" = '(100, 1)
--- | Bermudian dollar
-type instance I.Scale "BMD" "BMD" = '(100, 1)
-type instance I.Scale "BMD" "dollar" = '(1, 1)
-type instance I.Scale "BMD" "cent" = '(100, 1)
--- | Brunei dollar
-type instance I.Scale "BND" "BND" = '(100, 1)
-type instance I.Scale "BND" "dollar" = '(1, 1)
-type instance I.Scale "BND" "sen" = '(100, 1)
--- | Bolivian boliviano
-type instance I.Scale "BOB" "BOB" = '(100, 1)
-type instance I.Scale "BOB" "boliviano" = '(1, 1)
-type instance I.Scale "BOB" "centavo" = '(100, 1)
--- | Bolivian Mvdol
-type instance I.Scale "BOV" "BOV" = '(100, 1)
--- | Brazilian real
-type instance I.Scale "BRL" "BRL" = '(100, 1)
-type instance I.Scale "BRL" "real" = '(1, 1)
-type instance I.Scale "BRL" "centavo" = '(100, 1)
--- | Bahamian dollar
-type instance I.Scale "BSD" "BSD" = '(100, 1)
-type instance I.Scale "BSD" "dollar" = '(1, 1)
-type instance I.Scale "BSD" "cent" = '(1, 1)
--- | Bhutanese ngultrum
-type instance I.Scale "BTN" "BTN" = '(100, 1)
-type instance I.Scale "BTN" "ngultrum" = '(1, 1)
-type instance I.Scale "BTN" "chetrum" = '(100, 1)
--- | Botswana pula
-type instance I.Scale "BWP" "BWP" = '(100, 1)
-type instance I.Scale "BWP" "pula" = '(1, 1)
-type instance I.Scale "BWP" "thebe" = '(100, 1)
--- | Belarusian ruble
-type instance I.Scale "BYN" "BYN" = '(100, 1)
--- | Belarusian ruble
-type instance I.Scale "BYR" "BYR" = '(100, 1)
-type instance I.Scale "BYR" "ruble" = '(1, 1)
-type instance I.Scale "BYR" "kapyeyka" = '(100, 1)
--- | Belize dollar
-type instance I.Scale "BZD" "BZD" = '(100, 1)
-type instance I.Scale "BZD" "dollar" = '(1, 1)
-type instance I.Scale "BZD" "cent" = '(100, 1)
--- | Canadian dollar
-type instance I.Scale "CAD" "CAD" = '(100, 1)
-type instance I.Scale "CAD" "dollar" = '(1, 1)
-type instance I.Scale "CAD" "cent" = '(100, 1)
--- | Congolese franc
-type instance I.Scale "CDF" "CDF" = '(100, 1)
-type instance I.Scale "CDF" "franc" = '(1, 1)
-type instance I.Scale "CDF" "centime" = '(100, 1)
--- | WIR euro
-type instance I.Scale "CHE" "CHE" = '(100, 1)
--- | Swiss franc
-type instance I.Scale "CHF" "CHF" = '(100, 1)
-type instance I.Scale "CHF" "franc" = '(1, 1)
-type instance I.Scale "CHF" "rappen" = '(100, 1)
--- | WIR franc
-type instance I.Scale "CHW" "CHW" = '(100, 1)
--- | Chilean unidad de fomento
-type instance I.Scale "CLF" "CLF" = '(100, 1)
--- | Chilean peso
-type instance I.Scale "CLP" "CLP" = '(100, 1)
-type instance I.Scale "CLP" "peso" = '(1, 1)
-type instance I.Scale "CLP" "centavo" = '(100, 1)
--- | Chinese Renminbi
-type instance I.Scale "CNY" "CNY" = '(100, 1)
-type instance I.Scale "CNY" "yuan" = '(1, 1)
-type instance I.Scale "CNY" "fen" = '(100, 1)
--- | Colombian peso
-type instance I.Scale "COP" "COP" = '(100, 1)
-type instance I.Scale "COP" "peso" = '(1, 1)
-type instance I.Scale "COP" "centavo" = '(100, 1)
--- | Colombian unidad de valor real
-type instance I.Scale "COU" "COU" = '(100, 1)
--- | Costa Rican colon
-type instance I.Scale "CRC" "CRC" = '(100, 1)
-type instance I.Scale "CRC" "colon" = '(1, 1)
-type instance I.Scale "CRC" "centimo" = '(100, 1)
--- | Cuban peso convertible
-type instance I.Scale "CUC" "CUC" = '(100, 1)
-type instance I.Scale "CUC" "peso" = '(1, 1)
-type instance I.Scale "CUC" "centavo" = '(100, 1)
--- | Cuban peso
-type instance I.Scale "CUP" "CUP" = '(100, 1)
-type instance I.Scale "CUP" "peso" = '(1, 1)
-type instance I.Scale "CUP" "centavo" = '(100, 1)
--- | Cape Verdean escudo
-type instance I.Scale "CVE" "CVE" = '(100, 1)
-type instance I.Scale "CVE" "escudo" = '(1, 1)
-type instance I.Scale "CVE" "centavo" = '(100, 1)
--- | Czech koruna
-type instance I.Scale "CZK" "CZK" = '(100, 1)
-type instance I.Scale "CZK" "koruna" = '(1, 1)
-type instance I.Scale "CZK" "haler" = '(100, 1)
--- | Djiboutian franc
-type instance I.Scale "DJF" "DJF" = '(100, 1)
-type instance I.Scale "DJF" "franc" = '(1, 1)
-type instance I.Scale "DJF" "centime" = '(100, 1)
--- | Danish krone
-type instance I.Scale "DKK" "DKK" = '(100, 1)
-type instance I.Scale "DKK" "krone" = '(1, 1)
-type instance I.Scale "DKK" "ore" = '(100, 1)
--- | Dominican peso
-type instance I.Scale "DOP" "DOP" = '(100, 1)
-type instance I.Scale "DOP" "peso" = '(1, 1)
-type instance I.Scale "DOP" "centavo" = '(100, 1)
--- | Algerian dinar
-type instance I.Scale "DZD" "DZD" = '(100, 1)
-type instance I.Scale "DZD" "dinar" = '(1, 1)
-type instance I.Scale "DZD" "santeem" = '(100, 1)
--- | Egyptian pound
-type instance I.Scale "EGP" "EGP" = '(100, 1)
-type instance I.Scale "EGP" "pound" = '(1, 1)
-type instance I.Scale "EGP" "piastre" = '(100, 1)
--- | Eritrean nakfa
-type instance I.Scale "ERN" "ERN" = '(100, 1)
-type instance I.Scale "ERN" "nafka" = '(1, 1)
-type instance I.Scale "ERN" "cent" = '(100, 1)
--- | Ethiopian birr
-type instance I.Scale "ETB" "ETB" = '(100, 1)
-type instance I.Scale "ETB" "birr" = '(1, 1)
-type instance I.Scale "ETB" "santim" = '(100, 1)
--- | European euro
-type instance I.Scale "EUR" "EUR" = '(100, 1)
-type instance I.Scale "EUR" "euro" = '(1, 1)
-type instance I.Scale "EUR" "cent" = '(100, 1)
--- | Fijian dollar
-type instance I.Scale "FJD" "FJD" = '(100, 1)
--- | Falkland Islands pound
-type instance I.Scale "FKP" "FKP" = '(100, 1)
-type instance I.Scale "FKP" "pound" = '(1, 1)
-type instance I.Scale "FKP" "penny" = '(100, 1)
--- | Pound sterling
-type instance I.Scale "GBP" "GBP" = '(100, 1)
-type instance I.Scale "GBP" "pound" = '(1, 1)
-type instance I.Scale "GBP" "penny" = '(100, 1)
--- | Georgian lari
-type instance I.Scale "GEL" "GEL" = '(100, 1)
-type instance I.Scale "GEL" "lari" = '(1, 1)
-type instance I.Scale "GEL" "tetri" = '(100, 1)
--- | Ghanaian cedi
-type instance I.Scale "GHS" "GHS" = '(100, 1)
-type instance I.Scale "GHS" "cedi" = '(1, 1)
-type instance I.Scale "GHS" "pesewa" = '(100, 1)
--- | Gibraltar pound
-type instance I.Scale "GIP" "GIP" = '(100, 1)
-type instance I.Scale "GIP" "pound" = '(1, 1)
-type instance I.Scale "GIP" "penny" = '(100, 1)
--- | Gambian dalasi
-type instance I.Scale "GMD" "GMD" = '(100, 1)
-type instance I.Scale "GMD" "dalasi" = '(1, 1)
-type instance I.Scale "GMD" "butut" = '(100, 1)
--- | Guinean franc
-type instance I.Scale "GNF" "GNF" = '(100, 1)
-type instance I.Scale "GNF" "franc" = '(1, 1)
-type instance I.Scale "GNF" "centime" = '(100, 1)
--- | Guatemalan quetzal
-type instance I.Scale "GTQ" "GTQ" = '(100, 1)
-type instance I.Scale "GTQ" "quetzal" = '(1, 1)
-type instance I.Scale "GTQ" "centavo" = '(100, 1)
--- | Guyanese dollar
-type instance I.Scale "GYD" "GYD" = '(100, 1)
-type instance I.Scale "GYD" "dollar" = '(1, 1)
-type instance I.Scale "GYD" "cent" = '(100, 1)
--- | Hong Kong dollar
-type instance I.Scale "HKD" "HKD" = '(100, 1)
-type instance I.Scale "HKD" "dollar" = '(1, 1)
-type instance I.Scale "HKD" "cent" = '(100, 1)
--- | Honduran lempira
-type instance I.Scale "HNL" "HNL" = '(100, 1)
-type instance I.Scale "HNL" "lempira" = '(1, 1)
-type instance I.Scale "HNL" "centavo" = '(100, 1)
--- | Croatian kuna
-type instance I.Scale "HRK" "HRK" = '(100, 1)
-type instance I.Scale "HRK" "kuna" = '(1, 1)
-type instance I.Scale "HRK" "lipa" = '(100, 1)
--- | Haitian gourde
-type instance I.Scale "HTG" "HTG" = '(100, 1)
-type instance I.Scale "HTG" "gourde" = '(1, 1)
-type instance I.Scale "HTG" "centime" = '(1, 1)
--- | Hungarian forint
-type instance I.Scale "HUF" "HUF" = '(100, 1)
-type instance I.Scale "HUF" "forint" = '(1, 1)
-type instance I.Scale "HUF" "filler" = '(100, 1)
--- | Indonesian rupiah
-type instance I.Scale "IDR" "IDR" = '(100, 1)
-type instance I.Scale "IDR" "rupiah" = '(1, 1)
-type instance I.Scale "IDR" "sen" = '(100, 1)
--- | Israeli new shekel
-type instance I.Scale "ILS" "ILS" = '(100, 1)
-type instance I.Scale "ILS" "shekel" = '(1, 1)
-type instance I.Scale "ILS" "agora" = '(100, 1)
--- | Indian rupee
-type instance I.Scale "INR" "INR" = '(100, 1)
-type instance I.Scale "INR" "rupee" = '(1, 1)
-type instance I.Scale "INR" "paisa" = '(100, 1)
--- | Iraqi dinar
-type instance I.Scale "IQD" "IQD" = '(1000, 1)
-type instance I.Scale "IQD" "dinar" = '(1, 1)
-type instance I.Scale "IQD" "fils" = '(1000, 1)
--- | Iranian rial
-type instance I.Scale "IRR" "IRR" = '(100, 1)
-type instance I.Scale "IRR" "rial" = '(1, 1)
-type instance I.Scale "IRR" "dinar" = '(100, 1)
--- | Icelandic króna
-type instance I.Scale "ISK" "ISK" = '(100, 1)
-type instance I.Scale "ISK" "krona" = '(1, 1)
-type instance I.Scale "ISK" "eyrir" = '(100, 1)
--- | Jamaican dollar
-type instance I.Scale "JMD" "JMD" = '(100, 1)
-type instance I.Scale "JMD" "dollar" = '(1, 1)
-type instance I.Scale "JMD" "cent" = '(100, 1)
--- | Jordanian dinar
-type instance I.Scale "JOD" "JOD" = '(100, 1)
-type instance I.Scale "JOD" "dinar" = '(1, 1)
-type instance I.Scale "JOD" "piastre" = '(100, 1)
--- | Japanese yen
-type instance I.Scale "JPY" "JPY" = '(100, 1)
-type instance I.Scale "JPY" "yen" = '(1, 1)
-type instance I.Scale "JPY" "sen" = '(100, 1)
--- | Kenyan shilling
-type instance I.Scale "KES" "KES" = '(100, 1)
-type instance I.Scale "KES" "shilling" = '(1, 1)
-type instance I.Scale "KES" "cent" = '(100, 1)
--- | Kyrgyzstani som
-type instance I.Scale "KGS" "KGS" = '(100, 1)
-type instance I.Scale "KGS" "som" = '(1, 1)
-type instance I.Scale "KGS" "tyiyn" = '(100, 1)
--- | Cambodian riel
-type instance I.Scale "KHR" "KHR" = '(100, 1)
-type instance I.Scale "KHR" "riel" = '(1, 1)
-type instance I.Scale "KHR" "sen" = '(100, 1)
--- | Comorian franc
-type instance I.Scale "KMF" "KMF" = '(100, 1)
-type instance I.Scale "KMF" "franc" = '(1, 1)
-type instance I.Scale "KMF" "centime" = '(100, 1)
--- | North Korean won
-type instance I.Scale "KPW" "KPW" = '(100, 1)
-type instance I.Scale "KPW" "won" = '(1, 1)
-type instance I.Scale "KPW" "chon" = '(100, 1)
--- | South Korean won
-type instance I.Scale "KRW" "KRW" = '(100, 1)
-type instance I.Scale "KRW" "won" = '(1, 1)
-type instance I.Scale "KRW" "jeon" = '(100, 1)
--- | Kuwaiti dinar
-type instance I.Scale "KWD" "KWD" = '(1000, 1)
-type instance I.Scale "KWD" "dinar" = '(1, 1)
-type instance I.Scale "KWD" "fils" = '(1000, 1)
--- | Cayman Islands dollar
-type instance I.Scale "KYD" "KYD" = '(100, 1)
-type instance I.Scale "KYD" "dollar" = '(1, 1)
-type instance I.Scale "KYD" "cent" = '(100, 1)
--- | Kazakhstani tenge
-type instance I.Scale "KZT" "KZT" = '(100, 1)
-type instance I.Scale "KZT" "tenge" = '(1, 1)
-type instance I.Scale "KZT" "tiyin" = '(100, 1)
--- | Lao kip
-type instance I.Scale "LAK" "LAK" = '(100, 1)
-type instance I.Scale "LAK" "kip" = '(1, 1)
-type instance I.Scale "LAK" "att" = '(100, 1)
--- | Lebanese pound
-type instance I.Scale "LBP" "LBP" = '(100, 1)
-type instance I.Scale "LBP" "pound" = '(1, 1)
-type instance I.Scale "LBP" "piastre" = '(100, 1)
--- | Sri Lankan rupee
-type instance I.Scale "LKR" "LKR" = '(100, 1)
-type instance I.Scale "LKR" "rupee" = '(1, 1)
-type instance I.Scale "LKR" "cent" = '(100, 1)
--- | Liberian dollar
-type instance I.Scale "LRD" "LRD" = '(100, 1)
-type instance I.Scale "LRD" "dollar" = '(1, 1)
-type instance I.Scale "LRD" "cent" = '(100, 1)
--- | Lesotho loti
-type instance I.Scale "LSL" "LSL" = '(100, 1)
-type instance I.Scale "LSL" "loti" = '(1, 1)
-type instance I.Scale "LSL" "sente" = '(100, 1)
--- | Libyan dinar
-type instance I.Scale "LYD" "LYD" = '(100, 1)
-type instance I.Scale "LYD" "dinar" = '(1, 1)
-type instance I.Scale "LYD" "dirham" = '(1000, 1)
--- | Moroccan dirham
-type instance I.Scale "MAD" "MAD" = '(100, 1)
-type instance I.Scale "MAD" "dirham" = '(1, 1)
-type instance I.Scale "MAD" "centime" = '(100, 1)
--- | Moldovan leu
-type instance I.Scale "MDL" "MDL" = '(100, 1)
-type instance I.Scale "MDL" "leu" = '(100, 1)
-type instance I.Scale "MDL" "ban" = '(100, 1)
--- | Malagasy ariary
-type instance I.Scale "MGA" "MGA" = '(5, 1)
-type instance I.Scale "MGA" "ariary" = '(1, 1)
-type instance I.Scale "MGA" "iraimbilanja" = '(5, 1)
--- | Macedonian denar
-type instance I.Scale "MKD" "MKD" = '(100, 1)
-type instance I.Scale "MKD" "denar" = '(1, 1)
-type instance I.Scale "MKD" "deni" = '(100, 1)
--- | Myanmar kyat
-type instance I.Scale "MMK" "MMK" = '(100, 1)
-type instance I.Scale "MMK" "kyat" = '(1, 1)
-type instance I.Scale "MMK" "pya" = '(100, 1)
--- | Mongolian tugrik
-type instance I.Scale "MNT" "MNT" = '(100, 1)
-type instance I.Scale "MNT" "tugrik" = '(1, 1)
-type instance I.Scale "MNT" "mongo" = '(100, 1)
--- | Macanese pataca
-type instance I.Scale "MOP" "MOP" = '(100, 1)
-type instance I.Scale "MOP" "pataca" = '(1, 1)
-type instance I.Scale "MOP" "avo" = '(100, 1)
--- | Mauritanian ouguiya
-type instance I.Scale "MRO" "MRO" = '(5, 1)
-type instance I.Scale "MRO" "ouguiya" = '(1, 1)
-type instance I.Scale "MRO" "khoums" = '(5, 1)
--- | Mauritian rupee
-type instance I.Scale "MUR" "MUR" = '(100, 1)
-type instance I.Scale "MUR" "rupee" = '(1, 1)
-type instance I.Scale "MUR" "cent" = '(100, 1)
--- | Maldivian rufiyaa
-type instance I.Scale "MVR" "MVR" = '(100, 1)
-type instance I.Scale "MVR" "rufiyaa" = '(1, 1)
-type instance I.Scale "MVR" "laari" = '(100, 1)
--- | Malawian kwacha
-type instance I.Scale "MWK" "MWK" = '(100, 1)
-type instance I.Scale "MWK" "kwacha" = '(1, 1)
-type instance I.Scale "MWK" "tambala" = '(100, 1)
--- | Mexican peso
-type instance I.Scale "MXN" "MXN" = '(100, 1)
-type instance I.Scale "MXN" "peso" = '(1, 1)
-type instance I.Scale "MXN" "centavo" = '(100, 1)
--- | Mexican unidad de inversion
-type instance I.Scale "MXV" "MXV" = '(100, 1)
--- | Malaysian ringgit
-type instance I.Scale "MYR" "MYR" = '(100, 1)
-type instance I.Scale "MYR" "ringgit" = '(1, 1)
-type instance I.Scale "MYR" "sen" = '(100, 1)
--- | Mozambican metical
-type instance I.Scale "MZN" "MZN" = '(100, 1)
-type instance I.Scale "MZN" "metical" = '(1, 1)
-type instance I.Scale "MZN" "centavo" = '(100, 1)
--- | Namibian dollar
-type instance I.Scale "NAD" "NAD" = '(100, 1)
-type instance I.Scale "NAD" "dollar" = '(1, 1)
-type instance I.Scale "NAD" "cent" = '(100, 1)
--- | Nigerian naira
-type instance I.Scale "NGN" "NGN" = '(100, 1)
-type instance I.Scale "NGN" "naira" = '(1, 1)
-type instance I.Scale "NGN" "kobo" = '(100, 1)
--- | Nicaraguan cordoba
-type instance I.Scale "NIO" "NIO" = '(100, 1)
-type instance I.Scale "NIO" "cordoba" = '(1, 1)
-type instance I.Scale "NIO" "centavo" = '(100, 1)
--- | Norwegian krone
-type instance I.Scale "NOK" "NOK" = '(100, 1)
-type instance I.Scale "NOK" "krone" = '(1, 1)
-type instance I.Scale "NOK" "ore" = '(100, 1)
--- | Nepalese rupee
-type instance I.Scale "NPR" "NPR" = '(100, 1)
-type instance I.Scale "NPR" "rupee" = '(1, 1)
-type instance I.Scale "NPR" "paisa" = '(100, 1)
--- | New Zealand dollar
-type instance I.Scale "NZD" "NZD" = '(100, 1)
-type instance I.Scale "NZD" "dollar" = '(1, 1)
-type instance I.Scale "NZD" "cent" = '(100, 1)
--- | Omani rial
-type instance I.Scale "OMR" "OMR" = '(1000, 1)
-type instance I.Scale "OMR" "rial" = '(1, 1)
-type instance I.Scale "OMR" "baisa" = '(1000, 1)
--- | Panamenian balboa
-type instance I.Scale "PAB" "PAB" = '(100, 1)
-type instance I.Scale "PAB" "balboa" = '(1, 1)
-type instance I.Scale "PAB" "centesimo" = '(100, 1)
--- | Peruvian sol
-type instance I.Scale "PEN" "PEN" = '(100, 1)
-type instance I.Scale "PEN" "sol" = '(1, 1)
-type instance I.Scale "PEN" "centimo" = '(100, 1)
--- | Papua New Guinean kina
-type instance I.Scale "PGK" "PGK" = '(100, 1)
-type instance I.Scale "PGK" "kina" = '(1, 1)
-type instance I.Scale "PGK" "toea" = '(100, 1)
--- | Philippine peso
-type instance I.Scale "PHP" "PHP" = '(100, 1)
-type instance I.Scale "PHP" "peso" = '(1, 1)
-type instance I.Scale "PHP" "centavo" = '(100, 1)
--- | Pakistani rupee
-type instance I.Scale "PKR" "PKR" = '(100, 1)
-type instance I.Scale "PKR" "rupee" = '(1, 1)
-type instance I.Scale "PKR" "paisa" = '(100, 1)
--- | Polish zloty
-type instance I.Scale "PLN" "PLN" = '(100, 1)
-type instance I.Scale "PLN" "zloty" = '(1, 1)
-type instance I.Scale "PLN" "grosz" = '(100, 1)
--- | Paraguayan guarani
-type instance I.Scale "PYG" "PYG" = '(100, 1)
-type instance I.Scale "PYG" "guarani" = '(1, 1)
-type instance I.Scale "PYG" "centimo" = '(100, 1)
--- | Qatari riyal
-type instance I.Scale "QAR" "QAR" = '(100, 1)
-type instance I.Scale "QAR" "riyal" = '(1, 1)
-type instance I.Scale "QAR" "dirham" = '(100, 1)
--- | Romanian leu
-type instance I.Scale "RON" "RON" = '(100, 1)
-type instance I.Scale "RON" "leu" = '(1, 1)
-type instance I.Scale "RON" "ban" = '(100, 1)
--- | Serbian dinar
-type instance I.Scale "RSD" "RSD" = '(100, 1)
-type instance I.Scale "RSD" "dinar" = '(1, 1)
-type instance I.Scale "RSD" "para" = '(100, 1)
--- | Russian ruble
-type instance I.Scale "RUB" "RUB" = '(100, 1)
-type instance I.Scale "RUB" "ruble" = '(1, 1)
-type instance I.Scale "RUB" "kopek" = '(100, 1)
--- | Rwandan franc
-type instance I.Scale "RWF" "RWF" = '(100, 1)
-type instance I.Scale "RWF" "franc" = '(1, 1)
-type instance I.Scale "RWF" "centime" = '(100, 1)
--- | Saudi Arabian riyal
-type instance I.Scale "SAR" "SAR" = '(100, 1)
-type instance I.Scale "SAR" "riyal" = '(1, 1)
-type instance I.Scale "SAR" "halala" = '(100, 1)
--- | Solomon Islands dollar
-type instance I.Scale "SBD" "SBD" = '(100, 1)
-type instance I.Scale "SBD" "dollar" = '(100, 1)
-type instance I.Scale "SBD" "cent" = '(100, 1)
--- | Seychellois rupee
-type instance I.Scale "SCR" "SCR" = '(100, 1)
-type instance I.Scale "SCR" "rupee" = '(1, 1)
-type instance I.Scale "SCR" "cent" = '(100, 1)
--- | Sudanese pound
-type instance I.Scale "SDG" "SDG" = '(100, 1)
-type instance I.Scale "SDG" "pound" = '(1, 1)
-type instance I.Scale "SDG" "piastre" = '(100, 1)
--- | Swedish krona
-type instance I.Scale "SEK" "SEK" = '(100, 1)
-type instance I.Scale "SEK" "krona" = '(1, 1)
-type instance I.Scale "SEK" "ore" = '(100, 1)
--- | Singapore dollar
-type instance I.Scale "SGD" "SGD" = '(100, 1)
-type instance I.Scale "SGD" "dollar" = '(1, 1)
-type instance I.Scale "SGD" "cent" = '(100, 1)
--- | Saint Helena pound
-type instance I.Scale "SHP" "SHP" = '(100, 1)
-type instance I.Scale "SHP" "pound" = '(1, 1)
-type instance I.Scale "SHP" "penny" = '(100, 1)
--- | Sierra Leonean leone
-type instance I.Scale "SLL" "SLL" = '(100, 1)
-type instance I.Scale "SLL" "leone" = '(1, 1)
-type instance I.Scale "SLL" "cent" = '(100, 1)
--- | Somali shilling
-type instance I.Scale "SOS" "SOS" = '(100, 1)
-type instance I.Scale "SOS" "shilling" = '(1, 1)
-type instance I.Scale "SOS" "cent" = '(100, 1)
--- | Surinamese dollar
-type instance I.Scale "SRD" "SRD" = '(100, 1)
-type instance I.Scale "SRD" "dollar" = '(1, 1)
-type instance I.Scale "SRD" "cent" = '(100, 1)
--- | South Sudanese pound
-type instance I.Scale "SSP" "SSP" = '(100, 1)
-type instance I.Scale "SSP" "pound" = '(1, 1)
-type instance I.Scale "SSP" "piastre" = '(100, 1)
--- | Sao Tome and Principe dobra
-type instance I.Scale "STD" "STD" = '(100, 1)
-type instance I.Scale "STD" "dobra" = '(1, 1)
-type instance I.Scale "STD" "centimo" = '(100, 1)
--- | Salvadoran colon
-type instance I.Scale "SVC" "SVC" = '(100, 1)
-type instance I.Scale "SVC" "colon" = '(1, 1)
-type instance I.Scale "SVC" "centavo" = '(100, 1)
--- | Syrian pound
-type instance I.Scale "SYP" "SYP" = '(100, 1)
-type instance I.Scale "SYP" "pound" = '(1, 1)
-type instance I.Scale "SYP" "piastre" = '(100, 1)
--- | Swazi lilangeni
-type instance I.Scale "SZL" "SZL" = '(100, 1)
-type instance I.Scale "SZL" "lilangeni" = '(1, 1)
-type instance I.Scale "SZL" "cent" = '(100, 1)
--- | Thai baht
-type instance I.Scale "THB" "THB" = '(100, 1)
-type instance I.Scale "THB" "baht" = '(1, 1)
-type instance I.Scale "THB" "satang" = '(100, 1)
--- | Tajikistani somoni
-type instance I.Scale "TJS" "TJS" = '(100, 1)
-type instance I.Scale "TJS" "somoni" = '(1, 1)
-type instance I.Scale "TJS" "diram" = '(100, 1)
--- | Turkmen manat
-type instance I.Scale "TMT" "TMT" = '(100, 1)
-type instance I.Scale "TMT" "manat" = '(1, 1)
-type instance I.Scale "TMT" "tennesi" = '(100, 1)
--- | Tunisian dinar
-type instance I.Scale "TND" "TND" = '(1000, 1)
-type instance I.Scale "TND" "dinar" = '(1, 1)
-type instance I.Scale "TND" "millime" = '(1000, 1)
--- | Tongan pa’anga
-type instance I.Scale "TOP" "TOP" = '(100, 1)
-type instance I.Scale "TOP" "pa'anga" = '(1, 1)
-type instance I.Scale "TOP" "seniti" = '(100, 1)
--- | Turkish lira
-type instance I.Scale "TRY" "TRY" = '(100, 1)
-type instance I.Scale "TRY" "lira" = '(1, 1)
-type instance I.Scale "TRY" "kurus" = '(100, 1)
--- | Tobago Trinidad and Tobago dollar
-type instance I.Scale "TTD" "TTD" = '(100, 1)
-type instance I.Scale "TTD" "dollar" = '(1, 1)
-type instance I.Scale "TTD" "cent" = '(100, 1)
--- | New Taiwan dollar
-type instance I.Scale "TWD" "TWD" = '(100, 1)
-type instance I.Scale "TWD" "dollar" = '(1, 1)
-type instance I.Scale "TWD" "cent" = '(100, 1)
--- | Tanzanian shilling
-type instance I.Scale "TZS" "TZS" = '(100, 1)
-type instance I.Scale "TZS" "shilling" = '(1, 1)
-type instance I.Scale "TZS" "cent" = '(100, 1)
--- | Ukrainian hryvnia
-type instance I.Scale "UAH" "UAH" = '(100, 1)
-type instance I.Scale "UAH" "hryvnia" = '(1, 1)
-type instance I.Scale "UAH" "kopiyka" = '(100, 1)
--- | Ugandan shilling
-type instance I.Scale "UGX" "UGX" = '(100, 1)
-type instance I.Scale "UGX" "shilling" = '(1, 1)
-type instance I.Scale "UGX" "cent" = '(100, 1)
--- | United States dollar
-type instance I.Scale "USD" "USD" = '(100, 1)
-type instance I.Scale "USD" "dollar" = '(1, 1)
-type instance I.Scale "USD" "cent" = '(100, 1)
--- | United States dollar (next day)
-type instance I.Scale "USN" "USN" = '(100, 1)
--- | Uruguayan peso en unidades
-type instance I.Scale "UYI" "UYI" = '(100, 1)
--- | Uruguayan peso
-type instance I.Scale "UYU" "UYU" = '(100, 1)
-type instance I.Scale "UYU" "peso" = '(1, 1)
-type instance I.Scale "UYU" "centesimo" = '(100, 1)
--- | Uzbekistani som
-type instance I.Scale "UZS" "UZS" = '(100, 1)
-type instance I.Scale "UZS" "som" = '(1, 1)
-type instance I.Scale "UZS" "tiyin" = '(100, 1)
--- | Venezuelan bolivar
-type instance I.Scale "VEF" "VEF" = '(100, 1)
-type instance I.Scale "VEF" "bolivar" = '(1, 1)
-type instance I.Scale "VEF" "centimo" = '(100, 1)
--- | Vietnamese dong
-type instance I.Scale "VND" "VND" = '(10, 1)
-type instance I.Scale "VND" "dong" = '(1, 1)
-type instance I.Scale "VND" "hao" = '(10, 1)
--- | Vanuatu vatu
-type instance I.Scale "VUV" "VUV" = '(1, 1)
-type instance I.Scale "VUV" "vatu" = '(1, 1)
--- | Samoan tālā
-type instance I.Scale "WST" "WST" = '(100, 1)
-type instance I.Scale "WST" "tala" = '(1, 1)
-type instance I.Scale "WST" "sene" = '(100, 1)
--- | Central African CFA franc
-type instance I.Scale "XAF" "XAF" = '(100, 1)
-type instance I.Scale "XAF" "franc" = '(1, 1)
-type instance I.Scale "XAF" "centime" = '(100, 1)
--- | East Caribbean dollar
-type instance I.Scale "XCD" "XCD" = '(100, 1)
-type instance I.Scale "XCD" "dollar" = '(1, 1)
-type instance I.Scale "XCD" "cent" = '(100, 1)
--- | International Monetary Fund Special Drawing Right
-type instance I.Scale "XDR" "XDR" = '(100, 1)
--- | West African CFA franc
-type instance I.Scale "XOF" "XOF" = '(100, 1)
-type instance I.Scale "XOF" "franc" = '(1, 1)
-type instance I.Scale "XOF" "centime" = '(100, 1)
--- | CFP franc
-type instance I.Scale "XPF" "XPF" = '(100, 1)
-type instance I.Scale "XPF" "franc" = '(1, 1)
-type instance I.Scale "XPF" "centime" = '(100, 1)
--- | Sucre
-type instance I.Scale "XSU" "XSU" = '(100, 1)
--- | African Development Bank unit of account
-type instance I.Scale "XUA" "XUA" = '(100, 1)
--- | Yemeni rial
-type instance I.Scale "YER" "YER" = '(100, 1)
-type instance I.Scale "YER" "rial" = '(1, 1)
-type instance I.Scale "YER" "fils" = '(100, 1)
--- | South African rand
-type instance I.Scale "ZAR" "ZAR" = '(100, 1)
-type instance I.Scale "ZAR" "rand" = '(1, 1)
-type instance I.Scale "ZAR" "cent" = '(100, 1)
--- | Zambian kwacha
-type instance I.Scale "ZMW" "ZMW" = '(100, 1)
-type instance I.Scale "ZMW" "kwacha" = '(1, 1)
-type instance I.Scale "ZMW" "ngwee" = '(100, 1)
--- | Zimbawe dollar
-type instance I.Scale "ZWL" "ZWL" = '(100, 1)
-type instance I.Scale "ZWL" "dollar" = '(1, 1)
-type instance I.Scale "ZWL" "cent" = '(100, 1)
-
--- | Gold. No canonical smallest unit. Unusable instance.
-type instance I.Scale "XAU" "XAU" = I.ErrScaleNonCanonical "XAU"
-type instance I.Scale "XAU" "troy-ounce" = '(1, 1)
-type instance I.Scale "XAU" "grain" = '(480, 1)
-type instance I.Scale "XAU" "milligrain" = '(480000, 1)
-type instance I.Scale "XAU" "micrograin" = '(480000000, 1)
-type instance I.Scale "XAU" "kilogram" = '(31103477, 1000000000)
-type instance I.Scale "XAU" "gram" = '(31103477, 1000000)
-type instance I.Scale "XAU" "milligram" = '(31103477, 1000)
-type instance I.Scale "XAU" "microgram" = '(31103477, 1)
-
--- | Silver. No canonical smallest unit. Unusable instance.
-type instance I.Scale "XAG" "XAG" = I.ErrScaleNonCanonical "XAG"
-type instance I.Scale "XAG" "troy-ounce" = '(1, 1)
-type instance I.Scale "XAG" "grain" = '(480, 1)
-type instance I.Scale "XAG" "milligrain" = '(480000, 1)
-type instance I.Scale "XAG" "micrograin" = '(480000000, 1)
-type instance I.Scale "XAG" "kilogram" = '(31103477, 1000000000)
-type instance I.Scale "XAG" "gram" = '(31103477, 1000000)
-type instance I.Scale "XAG" "milligram" = '(31103477, 1000)
-type instance I.Scale "XAG" "microgram" = '(31103477, 1)
-
--- | Palladium. No canonical smallest unit. Unusable instance.
-type instance I.Scale "XPD" "XPD" = I.ErrScaleNonCanonical "XPD"
-type instance I.Scale "XPD" "troy-ounce" = '(1, 1)
-type instance I.Scale "XPD" "grain" = '(480, 1)
-type instance I.Scale "XPD" "milligrain" = '(480000, 1)
-type instance I.Scale "XPD" "micrograin" = '(480000000, 1)
-type instance I.Scale "XPD" "kilogram" = '(31103477, 1000000000)
-type instance I.Scale "XPD" "gram" = '(31103477, 1000000)
-type instance I.Scale "XPD" "milligram" = '(31103477, 1000)
-type instance I.Scale "XPD" "microgram" = '(31103477, 1)
-
--- | Platinum. No canonical smallest unit. Unusable instance.
-type instance I.Scale "XPT" "XPT" = I.ErrScaleNonCanonical "XPT"
-type instance I.Scale "XPT" "troy-ounce" = '(1, 1)
-type instance I.Scale "XPT" "grain" = '(480, 1)
-type instance I.Scale "XPT" "milligrain" = '(480000, 1)
-type instance I.Scale "XPT" "micrograin" = '(480000000, 1)
-type instance I.Scale "XPT" "kilogram" = '(31103477, 1000000000)
-type instance I.Scale "XPT" "gram" = '(31103477, 1000000)
-type instance I.Scale "XPT" "milligram" = '(31103477, 1000)
-type instance I.Scale "XPT" "microgram" = '(31103477, 1)
-
--- | Bitcoin
-type instance I.Scale "BTC" "BTC" = '(100000000, 1)
-type instance I.Scale "BTC" "bitcoin" = '(1, 1)
-type instance I.Scale "BTC" "millibitcoin" = '(1000, 1)
-type instance I.Scale "BTC" "satoshi" = '(100000000, 1)
-
--- | Bitcoin
-type instance I.Scale "XBT" "XBT" = '(100000000, 1)
-type instance I.Scale "XBT" "bitcoin" = '(1, 1)
-type instance I.Scale "XBT" "satoshi" = '(100000000, 1)
-
--- | Ether
-type instance I.Scale "ETH" "ETH" = '(1000000000000000000, 1)
-type instance I.Scale "ETH" "ether" = '(1, 1)
-type instance I.Scale "ETH" "kwei" = '(1000, 1)
-type instance I.Scale "ETH" "babbage" = '(1000, 1)
-type instance I.Scale "ETH" "mwei" = '(1000000, 1)
-type instance I.Scale "ETH" "lovelace" = '(1000000, 1)
-type instance I.Scale "ETH" "gwei" = '(1000000000, 1)
-type instance I.Scale "ETH" "shannon" = '(1000000000, 1)
-type instance I.Scale "ETH" "microether" = '(1000000000000, 1)
-type instance I.Scale "ETH" "szabo" = '(1000000000000, 1)
-type instance I.Scale "ETH" "finney" = '(1000000000000000, 1)
-type instance I.Scale "ETH" "milliether" = '(1000000000000000, 1)
-type instance I.Scale "ETH" "wei" = '(1000000000000000000, 1)
-
--- | Ada
-type instance I.Scale "ADA" "ADA" = '(1000000, 1)
-type instance I.Scale "ADA" "ada" = '(1, 1)
-type instance I.Scale "ADA" "lovelace" = '(1000000, 1)
-
--- | Litecoin
-type instance I.Scale "LTC" "LTC" = '(100000000, 1)
-type instance I.Scale "LTC" "litecoin" = '(1, 1)
-type instance I.Scale "LTC" "lite" = '(1000, 1)
-type instance I.Scale "LTC" "photon" = '(100000000, 1)
-
--- | Ripple
-type instance I.Scale "XRP" "XRP" = '(1000000, 1)
-type instance I.Scale "XRP" "ripple" = '(1, 1)
-type instance I.Scale "XRP" "drop" = '(1000000, 1)
-
--- | Monero
-type instance I.Scale "XMR" "XMR" = '(1000000000000, 1)
-type instance I.Scale "XMR" "monero" = '(1, 1)
-type instance I.Scale "XMR" "decinero" = '(10, 1)
-type instance I.Scale "XMR" "centinero" = '(100, 1)
-type instance I.Scale "XMR" "millinero" = '(1000, 1)
-type instance I.Scale "XMR" "micronero" = '(1000000, 1)
-type instance I.Scale "XMR" "nanonero" = '(1000000000, 1)
-type instance I.Scale "XMR" "piconero" = '(1000000000000, 1)
+-- can simply create a new 'I.UnitScale' instance, and /voilà/. If you want to add a
+-- new currency to the out-of-the-box offer, please request so in
+-- https://github.com/k0001/safe-money/issues and the authors will see to it.
+--
+-- This module offers plenty of documentation, but for a deep explanation of
+-- how all of the pieces fit together, please read
+-- <https://ren.zone/articles/safe-money>. Notice, however, that this library
+-- has changed a bit since that article was written. You can always see the
+-- [change log](https://github.com/k0001/safe-money/blob/master/changelog.md) to
+-- understand what has changed.
+--
+-- Also, keep in mind that useful instances for the many types defined by
+-- @safe-money@ can be found in these other libraries:
+--
+-- * [safe-money-aeson](https://hackage.haskell.org/package/safe-money-aeson):
+--   `FromJSON` and `ToJSON` instances (from the
+--   [aeson](https://hackage.haskell.org/package/aeson) library).
+--
+-- * [safe-money-cereal](https://hackage.haskell.org/package/safe-money-cereal):
+--   `Serialize` instances (from the
+--   [cereal](https://hackage.haskell.org/package/cereal) library).
+--
+-- * [safe-money-serialise](https://hackage.haskell.org/package/safe-money-serialise):
+--   `Serialise` instances (from the
+--   [serialise](https://hackage.haskell.org/package/serialise) library).
+--
+-- * [safe-money-store](https://hackage.haskell.org/package/safe-money-store):
+--   `Store` instances (from the
+--   [store](https://hackage.haskell.org/package/store) library).
+--
+-- * [safe-money-xmlbf](https://hackage.haskell.org/package/safe-money-xmlbf):
+--   `FromXml` and `ToXml` instances (from the
+--   [xmlbf](https://hackage.haskell.org/package/xmlbf) library).
+module Money
+ ( -- * Dense monetary values
+   I.Dense
+ , I.denseCurrency
+ , I.dense
+ , I.dense'
+ , I.denseFromDiscrete
+ , I.denseFromDecimal
+ , I.denseToDecimal
+   -- * Discrete monetary values
+ , I.Discrete
+ , I.Discrete'
+ , I.discrete
+ , I.discreteCurrency
+ , I.discreteFromDense
+ , I.discreteFromDecimal
+ , I.discreteToDecimal
+   -- * Currency scales
+ , I.Scale
+ , I.scaleFromRational
+ , I.scaleToRational
+ , I.scale
+ , I.UnitScale
+ , I.CurrencyScale
+ , I.GoodScale
+ , I.ErrScaleNonCanonical
+   -- * Currency exchange
+ , I.ExchangeRate
+ , I.exchangeRate
+ , I.exchange
+ , I.exchangeRateRecip
+ , I.exchangeRateFromDecimal
+ , I.exchangeRateToDecimal
+ , I.exchangeRateToRational
+   -- * Serializable representations
+ , I.SomeDense
+ , I.toSomeDense
+ , I.mkSomeDense
+ , I.fromSomeDense
+ , I.withSomeDense
+ , I.someDenseCurrency
+ , I.someDenseAmount
+ , I.SomeDiscrete
+ , I.toSomeDiscrete
+ , I.mkSomeDiscrete
+ , I.fromSomeDiscrete
+ , I.withSomeDiscrete
+ , I.someDiscreteCurrency
+ , I.someDiscreteScale
+ , I.someDiscreteAmount
+ , I.SomeExchangeRate
+ , I.toSomeExchangeRate
+ , I.mkSomeExchangeRate
+ , I.fromSomeExchangeRate
+ , I.withSomeExchangeRate
+ , I.someExchangeRateSrcCurrency
+ , I.someExchangeRateDstCurrency
+ , I.someExchangeRateRate
+ -- * Miscellaneous
+ , I.Approximation(..)
+ -- ** Decimal config
+ , I.DecimalConf(..)
+ , I.defaultDecimalConf
+ -- *** Separators
+ , I.Separators
+ , I.mkSeparators
+ , I.separatorsComma
+ , I.separatorsCommaDot
+ , I.separatorsCommaNarrownbsp
+ , I.separatorsCommaNbsp
+ , I.separatorsCommaThinsp
+ , I.separatorsCommaSpace
+ , I.separatorsDot
+ , I.separatorsDotComma
+ , I.separatorsDotNarrownbsp
+ , I.separatorsDotThinsp
+ , I.separatorsDotNbsp
+ , I.separatorsDotSpace
+ ) where
+
+import qualified Money.Internal as I
+
+--------------------------------------------------------------------------------
+-- Currency scales
+
+-- | United Arab Emirates dirham
+type instance I.CurrencyScale "AED" = I.UnitScale "AED" "fils"
+type instance I.UnitScale "AED" "dirham" = '(1, 1)
+type instance I.UnitScale "AED" "fils" = '(100, 1)
+-- | Afghan afghani
+type instance I.CurrencyScale "AFN" = I.UnitScale "AFN" "pul"
+type instance I.UnitScale "AFN" "afghani" = '(1, 1)
+type instance I.UnitScale "AFN" "pul" = '(100, 1)
+-- | Albanian lek
+type instance I.CurrencyScale "ALL" = I.UnitScale "ALL" "lek"
+type instance I.UnitScale "ALL" "lek" = '(1, 1)
+type instance I.UnitScale "ALL" "qindarke" = '(100, 1)
+-- | Armenian dram
+type instance I.CurrencyScale "AMD" = I.UnitScale "AMD" "luma"
+type instance I.UnitScale "AMD" "dram" = '(1, 1)
+type instance I.UnitScale "AMD" "luma" = '(100, 1)
+-- | Netherlands Antillean guilder
+type instance I.CurrencyScale "ANG" = I.UnitScale "AMD" "cent"
+type instance I.UnitScale "ANG" "guilder" = '(1, 1)
+type instance I.UnitScale "ANG" "cent" = '(100, 1)
+-- | Angolan kwanza
+type instance I.CurrencyScale "AOA" = I.UnitScale "AOA" "centimo"
+type instance I.UnitScale "AOA" "kwanza" = '(1, 1)
+type instance I.UnitScale "AOA" "centimo" = '(100, 1)
+-- | Argentine peso
+type instance I.CurrencyScale "ARS" = I.UnitScale "ARS" "centavo"
+type instance I.UnitScale "ARS" "peso" = '(1, 1)
+type instance I.UnitScale "ARS" "centavo" = '(100, 1)
+-- | Australian dollar
+type instance I.CurrencyScale "AUD" = I.UnitScale "AUD" "cent"
+type instance I.UnitScale "AUD" "dollar" = '(1, 1)
+type instance I.UnitScale "AUD" "cent" = '(100, 1)
+-- | Aruban florin
+type instance I.CurrencyScale "AWG" = I.UnitScale "AWG" "cent"
+type instance I.UnitScale "AWG" "florin" = '(1, 1)
+type instance I.UnitScale "AWG" "cent" = '(100, 1)
+-- | Azerbaijani manat
+type instance I.CurrencyScale "AZN" = I.UnitScale "AZN" "qapik"
+type instance I.UnitScale "AZN" "manat" = '(1, 1)
+type instance I.UnitScale "AZN" "qapik" = '(100, 1)
+-- | Bosnia and Herzegovina convertible mark
+type instance I.CurrencyScale "BAM" = I.UnitScale "BAM" "fenig"
+type instance I.UnitScale "BAM" "mark" = '(1, 1)
+type instance I.UnitScale "BAM" "fening" = '(100, 1)
+-- | Barbadian dollar
+type instance I.CurrencyScale "BBD" = I.UnitScale "BBD" "cent"
+type instance I.UnitScale "BBD" "dollar" = '(1, 1)
+type instance I.UnitScale "BBD" "cent" = '(100, 1)
+-- | Bangladeshi taka
+type instance I.CurrencyScale "BDT" = I.UnitScale "BDT" "paisa"
+type instance I.UnitScale "BDT" "taka" = '(1, 1)
+type instance I.UnitScale "BDT" "paisa" = '(100, 1)
+-- | Bulgarian lev
+type instance I.CurrencyScale "BGN" = I.UnitScale "BGN" "stotinka"
+type instance I.UnitScale "BGN" "lev" = '(1, 1)
+type instance I.UnitScale "BGN" "stotinka" = '(100, 1)
+-- | Bahraini dinar
+type instance I.CurrencyScale "BHD" = I.UnitScale "BHD" "fils"
+type instance I.UnitScale "BHD" "dinar" = '(1, 1)
+type instance I.UnitScale "BHD" "fils" = '(1000, 1)
+-- | Burundi franc
+type instance I.CurrencyScale "BIF" = I.UnitScale "BIF" "centime"
+type instance I.UnitScale "BIF" "franc" = '(1, 1)
+type instance I.UnitScale "BIF" "centime" = '(100, 1)
+-- | Bermudian dollar
+type instance I.CurrencyScale "BMD" = I.UnitScale "BMD" "cent"
+type instance I.UnitScale "BMD" "dollar" = '(1, 1)
+type instance I.UnitScale "BMD" "cent" = '(100, 1)
+-- | Brunei dollar
+type instance I.CurrencyScale "BND" = I.UnitScale "BND" "sen"
+type instance I.UnitScale "BND" "dollar" = '(1, 1)
+type instance I.UnitScale "BND" "sen" = '(100, 1)
+-- | Bolivian boliviano
+type instance I.CurrencyScale "BOB" = I.UnitScale "BOB" "centavo"
+type instance I.UnitScale "BOB" "boliviano" = '(1, 1)
+type instance I.UnitScale "BOB" "centavo" = '(100, 1)
+-- | Bolivian Mvdol
+type instance I.CurrencyScale "BOV" = '(100, 1)
+-- | Brazilian real
+type instance I.CurrencyScale "BRL" = I.UnitScale "BRL" "centavo"
+type instance I.UnitScale "BRL" "real" = '(1, 1)
+type instance I.UnitScale "BRL" "centavo" = '(100, 1)
+-- | Bahamian dollar
+type instance I.CurrencyScale "BSD" = I.UnitScale "BSD" "cent"
+type instance I.UnitScale "BSD" "dollar" = '(1, 1)
+type instance I.UnitScale "BSD" "cent" = '(1, 1)
+-- | Bhutanese ngultrum
+type instance I.CurrencyScale "BTN" = I.UnitScale "BTN" "chetrum"
+type instance I.UnitScale "BTN" "ngultrum" = '(1, 1)
+type instance I.UnitScale "BTN" "chetrum" = '(100, 1)
+-- | Botswana pula
+type instance I.CurrencyScale "BWP" = I.UnitScale "BWP" "thebe"
+type instance I.UnitScale "BWP" "pula" = '(1, 1)
+type instance I.UnitScale "BWP" "thebe" = '(100, 1)
+-- | Belarusian ruble
+type instance I.CurrencyScale "BYN" = I.UnitScale "BYN" "kapiejka"
+type instance I.UnitScale "BYN" "ruble" = '(1, 1)
+type instance I.UnitScale "BYN" "kapiejka" = '(100, 1)
+-- | Belarusian ruble
+type instance I.CurrencyScale "BYR" = I.UnitScale "BYR" "kapiejka"
+type instance I.UnitScale "BYR" "ruble" = '(1, 1)
+type instance I.UnitScale "BYR" "kapiejka" = '(100, 1)
+-- | Belize dollar
+type instance I.CurrencyScale "BZD" = I.UnitScale "BZD" "cent"
+type instance I.UnitScale "BZD" "dollar" = '(1, 1)
+type instance I.UnitScale "BZD" "cent" = '(100, 1)
+-- | Canadian dollar
+type instance I.CurrencyScale "CAD" = I.UnitScale "CAD" "cent"
+type instance I.UnitScale "CAD" "dollar" = '(1, 1)
+type instance I.UnitScale "CAD" "cent" = '(100, 1)
+-- | Congolese franc
+type instance I.CurrencyScale "CDF" = I.UnitScale "CDF" "centime"
+type instance I.UnitScale "CDF" "franc" = '(1, 1)
+type instance I.UnitScale "CDF" "centime" = '(100, 1)
+-- | WIR euro
+type instance I.CurrencyScale "CHE" = '(100, 1)
+-- | Swiss franc
+type instance I.CurrencyScale "CHF" = I.UnitScale "CHF" "rappen"
+type instance I.UnitScale "CHF" "franc" = '(1, 1)
+type instance I.UnitScale "CHF" "rappen" = '(100, 1)
+-- | WIR franc
+type instance I.CurrencyScale "CHW" = '(100, 1)
+-- | Chilean unidad de fomento
+type instance I.CurrencyScale "CLF" = '(10000, 1)
+-- | Chilean peso
+type instance I.CurrencyScale "CLP" = I.UnitScale "CLP" "centavo"
+type instance I.UnitScale "CLP" "peso" = '(1, 1)
+type instance I.UnitScale "CLP" "centavo" = '(100, 1)
+-- | Chinese Renminbi
+type instance I.CurrencyScale "CNY" = I.UnitScale "CNY" "fen"
+type instance I.UnitScale "CNY" "yuan" = '(1, 1)
+type instance I.UnitScale "CNY" "fen" = '(100, 1)
+-- | Colombian peso
+type instance I.CurrencyScale "COP" = I.UnitScale "COP" "centavo"
+type instance I.UnitScale "COP" "peso" = '(1, 1)
+type instance I.UnitScale "COP" "centavo" = '(100, 1)
+-- | Colombian unidad de valor real
+type instance I.CurrencyScale "COU" = '(100, 1)
+-- | Costa Rican colon
+type instance I.CurrencyScale "CRC" = I.UnitScale "CRC" "centimo"
+type instance I.UnitScale "CRC" "colon" = '(1, 1)
+type instance I.UnitScale "CRC" "centimo" = '(100, 1)
+-- | Cuban peso convertible
+type instance I.CurrencyScale "CUC" = I.UnitScale "CUC" "centavo"
+type instance I.UnitScale "CUC" "peso" = '(1, 1)
+type instance I.UnitScale "CUC" "centavo" = '(100, 1)
+-- | Cuban peso
+type instance I.CurrencyScale "CUP" = I.UnitScale "CUP" "centavo"
+type instance I.UnitScale "CUP" "peso" = '(1, 1)
+type instance I.UnitScale "CUP" "centavo" = '(100, 1)
+-- | Cape Verdean escudo
+type instance I.CurrencyScale "CVE" = I.UnitScale "CVE" "centavo"
+type instance I.UnitScale "CVE" "escudo" = '(1, 1)
+type instance I.UnitScale "CVE" "centavo" = '(100, 1)
+-- | Czech koruna
+type instance I.CurrencyScale "CZK" = I.UnitScale "CZK" "haler"
+type instance I.UnitScale "CZK" "koruna" = '(1, 1)
+type instance I.UnitScale "CZK" "haler" = '(100, 1)
+-- | Djiboutian franc
+type instance I.CurrencyScale "DJF" = I.UnitScale "DJF" "centime"
+type instance I.UnitScale "DJF" "franc" = '(1, 1)
+type instance I.UnitScale "DJF" "centime" = '(100, 1)
+-- | Danish krone
+type instance I.CurrencyScale "DKK" = I.UnitScale "DKK" "ore"
+type instance I.UnitScale "DKK" "krone" = '(1, 1)
+type instance I.UnitScale "DKK" "ore" = '(100, 1)
+-- | Dominican peso
+type instance I.CurrencyScale "DOP" = I.UnitScale "DOP" "centavo"
+type instance I.UnitScale "DOP" "peso" = '(1, 1)
+type instance I.UnitScale "DOP" "centavo" = '(100, 1)
+-- | Algerian dinar
+type instance I.CurrencyScale "DZD" = I.UnitScale "DZD" "santeem"
+type instance I.UnitScale "DZD" "dinar" = '(1, 1)
+type instance I.UnitScale "DZD" "santeem" = '(100, 1)
+-- | Egyptian pound
+type instance I.CurrencyScale "EGP" = I.UnitScale "EGP" "piastre"
+type instance I.UnitScale "EGP" "pound" = '(1, 1)
+type instance I.UnitScale "EGP" "piastre" = '(100, 1)
+-- | Eritrean nakfa
+type instance I.CurrencyScale "ERN" = I.UnitScale "ERN" "cent"
+type instance I.UnitScale "ERN" "nafka" = '(1, 1)
+type instance I.UnitScale "ERN" "cent" = '(100, 1)
+-- | Ethiopian birr
+type instance I.CurrencyScale "ETB" = I.UnitScale "ETB" "santim"
+type instance I.UnitScale "ETB" "birr" = '(1, 1)
+type instance I.UnitScale "ETB" "santim" = '(100, 1)
+-- | European euro
+type instance I.CurrencyScale "EUR" = I.UnitScale "EUR" "cent"
+type instance I.UnitScale "EUR" "euro" = '(1, 1)
+type instance I.UnitScale "EUR" "cent" = '(100, 1)
+-- | Fijian dollar
+type instance I.CurrencyScale "FJD" = I.UnitScale "FJD" "cent"
+type instance I.UnitScale "FJD" "dollar" = '(1, 1)
+type instance I.UnitScale "FJD" "cent" = '(100, 1)
+-- | Falkland Islands pound
+type instance I.CurrencyScale "FKP" = I.UnitScale "FKP" "penny"
+type instance I.UnitScale "FKP" "pound" = '(1, 1)
+type instance I.UnitScale "FKP" "penny" = '(100, 1)
+-- | Pound sterling
+type instance I.CurrencyScale "GBP" = I.UnitScale "GBP" "penny"
+type instance I.UnitScale "GBP" "pound" = '(1, 1)
+type instance I.UnitScale "GBP" "penny" = '(100, 1)
+-- | Georgian lari
+type instance I.CurrencyScale "GEL" = I.UnitScale "GEL" "tetri"
+type instance I.UnitScale "GEL" "lari" = '(1, 1)
+type instance I.UnitScale "GEL" "tetri" = '(100, 1)
+-- | Ghanaian cedi
+type instance I.CurrencyScale "GHS" = I.UnitScale "GHS" "pesewa"
+type instance I.UnitScale "GHS" "cedi" = '(1, 1)
+type instance I.UnitScale "GHS" "pesewa" = '(100, 1)
+-- | Gibraltar pound
+type instance I.CurrencyScale "GIP" = I.UnitScale "GIP" "penny"
+type instance I.UnitScale "GIP" "pound" = '(1, 1)
+type instance I.UnitScale "GIP" "penny" = '(100, 1)
+-- | Gambian dalasi
+type instance I.CurrencyScale "GMD" = I.UnitScale "GMD" "butut"
+type instance I.UnitScale "GMD" "dalasi" = '(1, 1)
+type instance I.UnitScale "GMD" "butut" = '(100, 1)
+-- | Guinean franc
+type instance I.CurrencyScale "GNF" = I.UnitScale "GNF" "centime"
+type instance I.UnitScale "GNF" "franc" = '(1, 1)
+type instance I.UnitScale "GNF" "centime" = '(100, 1)
+-- | Guatemalan quetzal
+type instance I.CurrencyScale "GTQ" = I.UnitScale "GTQ" "centavo"
+type instance I.UnitScale "GTQ" "quetzal" = '(1, 1)
+type instance I.UnitScale "GTQ" "centavo" = '(100, 1)
+-- | Guyanese dollar
+type instance I.CurrencyScale "GYD" = I.UnitScale "GYD" "cent"
+type instance I.UnitScale "GYD" "dollar" = '(1, 1)
+type instance I.UnitScale "GYD" "cent" = '(100, 1)
+-- | Hong Kong dollar
+type instance I.CurrencyScale "HKD" = I.UnitScale "HKD" "cent"
+type instance I.UnitScale "HKD" "dollar" = '(1, 1)
+type instance I.UnitScale "HKD" "cent" = '(100, 1)
+-- | Honduran lempira
+type instance I.CurrencyScale "HNL" = I.UnitScale "HNL" "centavo"
+type instance I.UnitScale "HNL" "lempira" = '(1, 1)
+type instance I.UnitScale "HNL" "centavo" = '(100, 1)
+-- | Croatian kuna
+type instance I.CurrencyScale "HRK" = I.UnitScale "HRK" "lipa"
+type instance I.UnitScale "HRK" "kuna" = '(1, 1)
+type instance I.UnitScale "HRK" "lipa" = '(100, 1)
+-- | Haitian gourde
+type instance I.CurrencyScale "HTG" = I.UnitScale "HTG" "centime"
+type instance I.UnitScale "HTG" "gourde" = '(1, 1)
+type instance I.UnitScale "HTG" "centime" = '(1, 1)
+-- | Hungarian forint
+type instance I.CurrencyScale "HUF" = I.UnitScale "HUF" "filler"
+type instance I.UnitScale "HUF" "forint" = '(1, 1)
+type instance I.UnitScale "HUF" "filler" = '(100, 1)
+-- | Indonesian rupiah
+type instance I.CurrencyScale "IDR" = I.UnitScale "IDR" "sen"
+type instance I.UnitScale "IDR" "rupiah" = '(1, 1)
+type instance I.UnitScale "IDR" "sen" = '(100, 1)
+-- | Israeli new shekel
+type instance I.CurrencyScale "ILS" = I.UnitScale "ILS" "agora"
+type instance I.UnitScale "ILS" "shekel" = '(1, 1)
+type instance I.UnitScale "ILS" "agora" = '(100, 1)
+-- | Indian rupee
+type instance I.CurrencyScale "INR" = I.UnitScale "INR" "paisa"
+type instance I.UnitScale "INR" "rupee" = '(1, 1)
+type instance I.UnitScale "INR" "paisa" = '(100, 1)
+-- | Iraqi dinar
+type instance I.CurrencyScale "IQD" = I.UnitScale "IQD" "fils"
+type instance I.UnitScale "IQD" "dinar" = '(1, 1)
+type instance I.UnitScale "IQD" "fils" = '(1000, 1)
+-- | Iranian rial
+type instance I.CurrencyScale "IRR" = I.UnitScale "IRR" "dinar"
+type instance I.UnitScale "IRR" "rial" = '(1, 1)
+type instance I.UnitScale "IRR" "dinar" = '(100, 1)
+-- | Icelandic króna
+type instance I.CurrencyScale "ISK" = I.UnitScale "ISK" "eyrir"
+type instance I.UnitScale "ISK" "krona" = '(1, 1)
+type instance I.UnitScale "ISK" "eyrir" = '(100, 1)
+-- | Jamaican dollar
+type instance I.CurrencyScale "JMD" = I.UnitScale "JMD" "cent"
+type instance I.UnitScale "JMD" "dollar" = '(1, 1)
+type instance I.UnitScale "JMD" "cent" = '(100, 1)
+-- | Jordanian dinar
+type instance I.CurrencyScale "JOD" = I.UnitScale "JOD" "piastre"
+type instance I.UnitScale "JOD" "dinar" = '(1, 1)
+type instance I.UnitScale "JOD" "piastre" = '(100, 1)
+-- | Japanese yen
+type instance I.CurrencyScale "JPY" = I.UnitScale "JPY" "sen"
+type instance I.UnitScale "JPY" "yen" = '(1, 1)
+type instance I.UnitScale "JPY" "sen" = '(100, 1)
+-- | Kenyan shilling
+type instance I.CurrencyScale "KES" = I.UnitScale "KES" "cent"
+type instance I.UnitScale "KES" "shilling" = '(1, 1)
+type instance I.UnitScale "KES" "cent" = '(100, 1)
+-- | Kyrgyzstani som
+type instance I.CurrencyScale "KGS" = I.UnitScale "KGS" "tyiyn"
+type instance I.UnitScale "KGS" "som" = '(1, 1)
+type instance I.UnitScale "KGS" "tyiyn" = '(100, 1)
+-- | Cambodian riel
+type instance I.CurrencyScale "KHR" = I.UnitScale "KHR" "sen"
+type instance I.UnitScale "KHR" "riel" = '(1, 1)
+type instance I.UnitScale "KHR" "sen" = '(100, 1)
+-- | Comorian franc
+type instance I.CurrencyScale "KMF" = I.UnitScale "KMF" "centime"
+type instance I.UnitScale "KMF" "franc" = '(1, 1)
+type instance I.UnitScale "KMF" "centime" = '(100, 1)
+-- | North Korean won
+type instance I.CurrencyScale "KPW" = I.UnitScale "KPW" "chon"
+type instance I.UnitScale "KPW" "won" = '(1, 1)
+type instance I.UnitScale "KPW" "chon" = '(100, 1)
+-- | South Korean won
+type instance I.CurrencyScale "KRW" = I.UnitScale "KRW" "jeon"
+type instance I.UnitScale "KRW" "won" = '(1, 1)
+type instance I.UnitScale "KRW" "jeon" = '(100, 1)
+-- | Kuwaiti dinar
+type instance I.CurrencyScale "KWD" = I.UnitScale "KWD" "fils"
+type instance I.UnitScale "KWD" "dinar" = '(1, 1)
+type instance I.UnitScale "KWD" "fils" = '(1000, 1)
+-- | Cayman Islands dollar
+type instance I.CurrencyScale "KYD" = I.UnitScale "KYD" "cent"
+type instance I.UnitScale "KYD" "dollar" = '(1, 1)
+type instance I.UnitScale "KYD" "cent" = '(100, 1)
+-- | Kazakhstani tenge
+type instance I.CurrencyScale "KZT" = I.UnitScale "KZT" "tiyin"
+type instance I.UnitScale "KZT" "tenge" = '(1, 1)
+type instance I.UnitScale "KZT" "tiyin" = '(100, 1)
+-- | Lao kip
+type instance I.CurrencyScale "LAK" = I.UnitScale "LAK" "att"
+type instance I.UnitScale "LAK" "kip" = '(1, 1)
+type instance I.UnitScale "LAK" "att" = '(100, 1)
+-- | Lebanese pound
+type instance I.CurrencyScale "LBP" = I.UnitScale "LBP" "piastre"
+type instance I.UnitScale "LBP" "pound" = '(1, 1)
+type instance I.UnitScale "LBP" "piastre" = '(100, 1)
+-- | Sri Lankan rupee
+type instance I.CurrencyScale "LKR" = I.UnitScale "LKR" "cent"
+type instance I.UnitScale "LKR" "rupee" = '(1, 1)
+type instance I.UnitScale "LKR" "cent" = '(100, 1)
+-- | Liberian dollar
+type instance I.CurrencyScale "LRD" = I.UnitScale "LRD" "cent"
+type instance I.UnitScale "LRD" "dollar" = '(1, 1)
+type instance I.UnitScale "LRD" "cent" = '(100, 1)
+-- | Lesotho loti
+type instance I.CurrencyScale "LSL" = I.UnitScale "LSL" "sente"
+type instance I.UnitScale "LSL" "loti" = '(1, 1)
+type instance I.UnitScale "LSL" "sente" = '(100, 1)
+-- | Libyan dinar
+type instance I.CurrencyScale "LYD" = I.UnitScale "LYD" "dirham"
+type instance I.UnitScale "LYD" "dinar" = '(1, 1)
+type instance I.UnitScale "LYD" "dirham" = '(1000, 1)
+-- | Moroccan dirham
+type instance I.CurrencyScale "MAD" = I.UnitScale "MAD" "centime"
+type instance I.UnitScale "MAD" "dirham" = '(1, 1)
+type instance I.UnitScale "MAD" "centime" = '(100, 1)
+-- | Moldovan leu
+type instance I.CurrencyScale "MDL" = I.UnitScale "MDL" "ban"
+type instance I.UnitScale "MDL" "leu" = '(100, 1)
+type instance I.UnitScale "MDL" "ban" = '(100, 1)
+-- | Malagasy ariary
+type instance I.CurrencyScale "MGA" = I.UnitScale "MGA" "iraimbilanja"
+type instance I.UnitScale "MGA" "ariary" = '(1, 1)
+type instance I.UnitScale "MGA" "iraimbilanja" = '(5, 1)
+-- | Macedonian denar
+type instance I.CurrencyScale "MKD" = I.UnitScale "MKD" "deni"
+type instance I.UnitScale "MKD" "denar" = '(1, 1)
+type instance I.UnitScale "MKD" "deni" = '(100, 1)
+-- | Myanmar kyat
+type instance I.CurrencyScale "MMK" = I.UnitScale "MMK" "pya"
+type instance I.UnitScale "MMK" "kyat" = '(1, 1)
+type instance I.UnitScale "MMK" "pya" = '(100, 1)
+-- | Mongolian tugrik
+type instance I.CurrencyScale "MNT" = I.UnitScale "MNT" "mongo"
+type instance I.UnitScale "MNT" "tugrik" = '(1, 1)
+type instance I.UnitScale "MNT" "mongo" = '(100, 1)
+-- | Macanese pataca
+type instance I.CurrencyScale "MOP" = I.UnitScale "MOP" "avo"
+type instance I.UnitScale "MOP" "pataca" = '(1, 1)
+type instance I.UnitScale "MOP" "avo" = '(100, 1)
+-- | Mauritanian ouguiya
+type instance I.CurrencyScale "MRO" = I.UnitScale "MRO" "khoums"
+type instance I.UnitScale "MRO" "ouguiya" = '(1, 1)
+type instance I.UnitScale "MRO" "khoums" = '(5, 1)
+-- | Mauritian rupee
+type instance I.CurrencyScale "MUR" = I.UnitScale "MUR" "cent"
+type instance I.UnitScale "MUR" "rupee" = '(1, 1)
+type instance I.UnitScale "MUR" "cent" = '(100, 1)
+-- | Maldivian rufiyaa
+type instance I.CurrencyScale "MVR" = I.UnitScale "MVR" "laari"
+type instance I.UnitScale "MVR" "rufiyaa" = '(1, 1)
+type instance I.UnitScale "MVR" "laari" = '(100, 1)
+-- | Malawian kwacha
+type instance I.CurrencyScale "MWK" = I.UnitScale "MWK" "tambala"
+type instance I.UnitScale "MWK" "kwacha" = '(1, 1)
+type instance I.UnitScale "MWK" "tambala" = '(100, 1)
+-- | Mexican peso
+type instance I.CurrencyScale "MXN" = I.UnitScale "MXN" "centavo"
+type instance I.UnitScale "MXN" "peso" = '(1, 1)
+type instance I.UnitScale "MXN" "centavo" = '(100, 1)
+-- | Mexican unidad de inversion
+type instance I.CurrencyScale "MXV" = '(100, 1)
+-- | Malaysian ringgit
+type instance I.CurrencyScale "MYR" = I.UnitScale "MYR" "sen"
+type instance I.UnitScale "MYR" "ringgit" = '(1, 1)
+type instance I.UnitScale "MYR" "sen" = '(100, 1)
+-- | Mozambican metical
+type instance I.CurrencyScale "MZN" = I.UnitScale "MZN" "centavo"
+type instance I.UnitScale "MZN" "metical" = '(1, 1)
+type instance I.UnitScale "MZN" "centavo" = '(100, 1)
+-- | Namibian dollar
+type instance I.CurrencyScale "NAD" = I.UnitScale "NAD" "cent"
+type instance I.UnitScale "NAD" "dollar" = '(1, 1)
+type instance I.UnitScale "NAD" "cent" = '(100, 1)
+-- | Nigerian naira
+type instance I.CurrencyScale "NGN" = I.UnitScale "NGN" "kobo"
+type instance I.UnitScale "NGN" "naira" = '(1, 1)
+type instance I.UnitScale "NGN" "kobo" = '(100, 1)
+-- | Nicaraguan cordoba
+type instance I.CurrencyScale "NIO" = I.UnitScale "NIO" "centavo"
+type instance I.UnitScale "NIO" "cordoba" = '(1, 1)
+type instance I.UnitScale "NIO" "centavo" = '(100, 1)
+-- | Norwegian krone
+type instance I.CurrencyScale "NOK" = I.UnitScale "NOK" "ore"
+type instance I.UnitScale "NOK" "krone" = '(1, 1)
+type instance I.UnitScale "NOK" "ore" = '(100, 1)
+-- | Nepalese rupee
+type instance I.CurrencyScale "NPR" = I.UnitScale "NPR" "paisa"
+type instance I.UnitScale "NPR" "rupee" = '(1, 1)
+type instance I.UnitScale "NPR" "paisa" = '(100, 1)
+-- | New Zealand dollar
+type instance I.CurrencyScale "NZD" = I.UnitScale "NZD" "cent"
+type instance I.UnitScale "NZD" "dollar" = '(1, 1)
+type instance I.UnitScale "NZD" "cent" = '(100, 1)
+-- | Omani rial
+type instance I.CurrencyScale "OMR" = I.UnitScale "OMR" "baisa"
+type instance I.UnitScale "OMR" "rial" = '(1, 1)
+type instance I.UnitScale "OMR" "baisa" = '(1000, 1)
+-- | Panamenian balboa
+type instance I.CurrencyScale "PAB" = I.UnitScale "PAB" "centesimo"
+type instance I.UnitScale "PAB" "balboa" = '(1, 1)
+type instance I.UnitScale "PAB" "centesimo" = '(100, 1)
+-- | Peruvian sol
+type instance I.CurrencyScale "PEN" = I.UnitScale "PEN" "centimo"
+type instance I.UnitScale "PEN" "sol" = '(1, 1)
+type instance I.UnitScale "PEN" "centimo" = '(100, 1)
+-- | Papua New Guinean kina
+type instance I.CurrencyScale "PGK" = I.UnitScale "PGK" "toea"
+type instance I.UnitScale "PGK" "kina" = '(1, 1)
+type instance I.UnitScale "PGK" "toea" = '(100, 1)
+-- | Philippine peso
+type instance I.CurrencyScale "PHP" = I.UnitScale "PHP" "centavo"
+type instance I.UnitScale "PHP" "peso" = '(1, 1)
+type instance I.UnitScale "PHP" "centavo" = '(100, 1)
+-- | Pakistani rupee
+type instance I.CurrencyScale "PKR" = I.UnitScale "PKR" "paisa"
+type instance I.UnitScale "PKR" "rupee" = '(1, 1)
+type instance I.UnitScale "PKR" "paisa" = '(100, 1)
+-- | Polish zloty
+type instance I.CurrencyScale "PLN" = I.UnitScale "PLN" "grosz"
+type instance I.UnitScale "PLN" "zloty" = '(1, 1)
+type instance I.UnitScale "PLN" "grosz" = '(100, 1)
+-- | Paraguayan guarani
+type instance I.CurrencyScale "PYG" = I.UnitScale "PYG" "centimo"
+type instance I.UnitScale "PYG" "guarani" = '(1, 1)
+type instance I.UnitScale "PYG" "centimo" = '(100, 1)
+-- | Qatari riyal
+type instance I.CurrencyScale "QAR" = I.UnitScale "QAR" "dirham"
+type instance I.UnitScale "QAR" "riyal" = '(1, 1)
+type instance I.UnitScale "QAR" "dirham" = '(100, 1)
+-- | Romanian leu
+type instance I.CurrencyScale "RON" = I.UnitScale "RON" "ban"
+type instance I.UnitScale "RON" "leu" = '(1, 1)
+type instance I.UnitScale "RON" "ban" = '(100, 1)
+-- | Serbian dinar
+type instance I.CurrencyScale "RSD" = I.UnitScale "RSD" "para"
+type instance I.UnitScale "RSD" "dinar" = '(1, 1)
+type instance I.UnitScale "RSD" "para" = '(100, 1)
+-- | Russian ruble
+type instance I.CurrencyScale "RUB" = I.UnitScale "RUB" "kopek"
+type instance I.UnitScale "RUB" "ruble" = '(1, 1)
+type instance I.UnitScale "RUB" "kopek" = '(100, 1)
+-- | Rwandan franc
+type instance I.CurrencyScale "RWF" = I.UnitScale "RWF" "centime"
+type instance I.UnitScale "RWF" "franc" = '(1, 1)
+type instance I.UnitScale "RWF" "centime" = '(100, 1)
+-- | Saudi Arabian riyal
+type instance I.CurrencyScale "SAR" = I.UnitScale "SAR" "halala"
+type instance I.UnitScale "SAR" "riyal" = '(1, 1)
+type instance I.UnitScale "SAR" "halala" = '(100, 1)
+-- | Solomon Islands dollar
+type instance I.CurrencyScale "SBD" = I.UnitScale "SBD" "cent"
+type instance I.UnitScale "SBD" "dollar" = '(100, 1)
+type instance I.UnitScale "SBD" "cent" = '(100, 1)
+-- | Seychellois rupee
+type instance I.CurrencyScale "SCR" = I.UnitScale "SCR" "cent"
+type instance I.UnitScale "SCR" "rupee" = '(1, 1)
+type instance I.UnitScale "SCR" "cent" = '(100, 1)
+-- | Sudanese pound
+type instance I.CurrencyScale "SDG" = I.UnitScale "SDG" "piastre"
+type instance I.UnitScale "SDG" "pound" = '(1, 1)
+type instance I.UnitScale "SDG" "piastre" = '(100, 1)
+-- | Swedish krona
+type instance I.CurrencyScale "SEK" = I.UnitScale "SEK" "ore"
+type instance I.UnitScale "SEK" "krona" = '(1, 1)
+type instance I.UnitScale "SEK" "ore" = '(100, 1)
+-- | Singapore dollar
+type instance I.CurrencyScale "SGD" = I.UnitScale "SGD" "cent"
+type instance I.UnitScale "SGD" "dollar" = '(1, 1)
+type instance I.UnitScale "SGD" "cent" = '(100, 1)
+-- | Saint Helena pound
+type instance I.CurrencyScale "SHP" = I.UnitScale "SHP" "penny"
+type instance I.UnitScale "SHP" "pound" = '(1, 1)
+type instance I.UnitScale "SHP" "penny" = '(100, 1)
+-- | Sierra Leonean leone
+type instance I.CurrencyScale "SLL" = I.UnitScale "SLL" "cent"
+type instance I.UnitScale "SLL" "leone" = '(1, 1)
+type instance I.UnitScale "SLL" "cent" = '(100, 1)
+-- | Somali shilling
+type instance I.CurrencyScale "SOS" = I.UnitScale "SOS" "cent"
+type instance I.UnitScale "SOS" "shilling" = '(1, 1)
+type instance I.UnitScale "SOS" "cent" = '(100, 1)
+-- | Surinamese dollar
+type instance I.CurrencyScale "SRD" = I.UnitScale "SRD" "cent"
+type instance I.UnitScale "SRD" "dollar" = '(1, 1)
+type instance I.UnitScale "SRD" "cent" = '(100, 1)
+-- | South Sudanese pound
+type instance I.CurrencyScale "SSP" = I.UnitScale "SSP" "piastre"
+type instance I.UnitScale "SSP" "pound" = '(1, 1)
+type instance I.UnitScale "SSP" "piastre" = '(100, 1)
+-- | Sao Tome and Principe dobra
+type instance I.CurrencyScale "STD" = I.UnitScale "STD" "centimo"
+type instance I.UnitScale "STD" "dobra" = '(1, 1)
+type instance I.UnitScale "STD" "centimo" = '(100, 1)
+-- | Salvadoran colon
+type instance I.CurrencyScale "SVC" = I.UnitScale "SVC" "centavo"
+type instance I.UnitScale "SVC" "colon" = '(1, 1)
+type instance I.UnitScale "SVC" "centavo" = '(100, 1)
+-- | Syrian pound
+type instance I.CurrencyScale "SYP" = I.UnitScale "SYP" "piastre"
+type instance I.UnitScale "SYP" "pound" = '(1, 1)
+type instance I.UnitScale "SYP" "piastre" = '(100, 1)
+-- | Swazi lilangeni
+type instance I.CurrencyScale "SZL" = I.UnitScale "SZL" "cent"
+type instance I.UnitScale "SZL" "lilangeni" = '(1, 1)
+type instance I.UnitScale "SZL" "cent" = '(100, 1)
+-- | Thai baht
+type instance I.CurrencyScale "THB" = I.UnitScale "THB" "satang"
+type instance I.UnitScale "THB" "baht" = '(1, 1)
+type instance I.UnitScale "THB" "satang" = '(100, 1)
+-- | Tajikistani somoni
+type instance I.CurrencyScale "TJS" = I.UnitScale "TJS" "diram"
+type instance I.UnitScale "TJS" "somoni" = '(1, 1)
+type instance I.UnitScale "TJS" "diram" = '(100, 1)
+-- | Turkmen manat
+type instance I.CurrencyScale "TMT" = I.UnitScale "TMT" "tennesi"
+type instance I.UnitScale "TMT" "manat" = '(1, 1)
+type instance I.UnitScale "TMT" "tennesi" = '(100, 1)
+-- | Tunisian dinar
+type instance I.CurrencyScale "TND" = I.UnitScale "TND" "millime"
+type instance I.UnitScale "TND" "dinar" = '(1, 1)
+type instance I.UnitScale "TND" "millime" = '(1000, 1)
+-- | Tongan pa’anga
+type instance I.CurrencyScale "TOP" = I.UnitScale "TOP" "seniti"
+type instance I.UnitScale "TOP" "pa'anga" = '(1, 1)
+type instance I.UnitScale "TOP" "seniti" = '(100, 1)
+-- | Turkish lira
+type instance I.CurrencyScale "TRY" = I.UnitScale "TRY" "kurus"
+type instance I.UnitScale "TRY" "lira" = '(1, 1)
+type instance I.UnitScale "TRY" "kurus" = '(100, 1)
+-- | Tobago Trinidad and Tobago dollar
+type instance I.CurrencyScale "TTD" = I.UnitScale "TTD" "cent"
+type instance I.UnitScale "TTD" "dollar" = '(1, 1)
+type instance I.UnitScale "TTD" "cent" = '(100, 1)
+-- | New Taiwan dollar
+type instance I.CurrencyScale "TWD" = I.UnitScale "TWD" "cent"
+type instance I.UnitScale "TWD" "dollar" = '(1, 1)
+type instance I.UnitScale "TWD" "cent" = '(100, 1)
+-- | Tanzanian shilling
+type instance I.CurrencyScale "TZS" = I.UnitScale "TZS" "cent"
+type instance I.UnitScale "TZS" "shilling" = '(1, 1)
+type instance I.UnitScale "TZS" "cent" = '(100, 1)
+-- | Ukrainian hryvnia
+type instance I.CurrencyScale "UAH" = I.UnitScale "UAH" "kopiyka"
+type instance I.UnitScale "UAH" "hryvnia" = '(1, 1)
+type instance I.UnitScale "UAH" "kopiyka" = '(100, 1)
+-- | Ugandan shilling
+type instance I.CurrencyScale "UGX" = I.UnitScale "UGX" "cent"
+type instance I.UnitScale "UGX" "shilling" = '(1, 1)
+type instance I.UnitScale "UGX" "cent" = '(100, 1)
+-- | United States dollar
+type instance I.CurrencyScale "USD" = I.UnitScale "USD" "cent"
+type instance I.UnitScale "USD" "dollar" = '(1, 1)
+type instance I.UnitScale "USD" "cent" = '(100, 1)
+-- | United States dollar (next day)
+type instance I.CurrencyScale "USN" = I.UnitScale "USN" "cent"
+type instance I.UnitScale "USN" "dollar" = '(1, 1)
+type instance I.UnitScale "USN" "cent" = '(100, 1)
+-- | Uruguayan peso en unidades indexadas
+type instance I.CurrencyScale "UYI" = '(1, 1)
+-- | Uruguayan peso
+type instance I.CurrencyScale "UYU" = I.UnitScale "UYU" "centesimo"
+type instance I.UnitScale "UYU" "peso" = '(1, 1)
+type instance I.UnitScale "UYU" "centesimo" = '(100, 1)
+-- | Uruguayan unidad previsional
+type instance I.CurrencyScale "UYW" = '(10000, 1)
+-- | Uzbekistani som
+type instance I.CurrencyScale "UZS" = I.UnitScale "UZS" "tiyin"
+type instance I.UnitScale "UZS" "som" = '(1, 1)
+type instance I.UnitScale "UZS" "tiyin" = '(100, 1)
+-- | Venezuelan bolivar fuerte
+type instance I.CurrencyScale "VEF" = I.UnitScale "VEF" "centimo"
+type instance I.UnitScale "VEF" "bolivar" = '(1, 1)
+type instance I.UnitScale "VEF" "centimo" = '(100, 1)
+-- | Venezuelan bolivar soberano
+type instance I.CurrencyScale "VES" = I.UnitScale "VES" "centimo"
+type instance I.UnitScale "VES" "bolivar" = '(1, 1)
+type instance I.UnitScale "VES" "centimo" = '(100, 1)
+-- | Vietnamese dong
+type instance I.CurrencyScale "VND" = I.UnitScale "VND" "hao"
+type instance I.UnitScale "VND" "dong" = '(1, 1)
+type instance I.UnitScale "VND" "hao" = '(10, 1)
+-- | Vanuatu vatu
+type instance I.CurrencyScale "VUV" = I.UnitScale "VUV" "vatu"
+type instance I.UnitScale "VUV" "vatu" = '(1, 1)
+-- | Samoan tālā
+type instance I.CurrencyScale "WST" = I.UnitScale "WST" "sene"
+type instance I.UnitScale "WST" "tala" = '(1, 1)
+type instance I.UnitScale "WST" "sene" = '(100, 1)
+-- | Central African CFA franc
+type instance I.CurrencyScale "XAF" = I.UnitScale "XAF" "centime"
+type instance I.UnitScale "XAF" "franc" = '(1, 1)
+type instance I.UnitScale "XAF" "centime" = '(100, 1)
+-- | East Caribbean dollar
+type instance I.CurrencyScale "XCD" = I.UnitScale "XCD" "cent"
+type instance I.UnitScale "XCD" "dollar" = '(1, 1)
+type instance I.UnitScale "XCD" "cent" = '(100, 1)
+-- | International Monetary Fund Special Drawing Right
+type instance I.CurrencyScale "XDR" = '(1, 1)
+-- | West African CFA franc
+type instance I.CurrencyScale "XOF" = I.UnitScale "XOF" "centime"
+type instance I.UnitScale "XOF" "franc" = '(1, 1)
+type instance I.UnitScale "XOF" "centime" = '(100, 1)
+-- | CFP franc
+type instance I.CurrencyScale "XPF" = I.UnitScale "XPF" "centime"
+type instance I.UnitScale "XPF" "franc" = '(1, 1)
+type instance I.UnitScale "XPF" "centime" = '(100, 1)
+-- | Sucre
+type instance I.CurrencyScale "XSU" = '(1, 1)
+-- | African Development Bank unit of account
+type instance I.CurrencyScale "XUA" = '(1, 1)
+-- | Yemeni rial
+type instance I.CurrencyScale "YER" = I.UnitScale "YER" "fils"
+type instance I.UnitScale "YER" "rial" = '(1, 1)
+type instance I.UnitScale "YER" "fils" = '(100, 1)
+-- | South African rand
+type instance I.CurrencyScale "ZAR" = I.UnitScale "ZAR" "cent"
+type instance I.UnitScale "ZAR" "rand" = '(1, 1)
+type instance I.UnitScale "ZAR" "cent" = '(100, 1)
+-- | Zambian kwacha
+type instance I.CurrencyScale "ZMW" = I.UnitScale "ZMW" "ngwee"
+type instance I.UnitScale "ZMW" "kwacha" = '(1, 1)
+type instance I.UnitScale "ZMW" "ngwee" = '(100, 1)
+-- | Zimbawe dollar
+type instance I.CurrencyScale "ZWL" = I.UnitScale "ZWL" "cent"
+type instance I.UnitScale "ZWL" "dollar" = '(1, 1)
+type instance I.UnitScale "ZWL" "cent" = '(100, 1)
+-- | Gold. No canonical smallest unit. Unusable instance.
+type instance I.CurrencyScale "XAU" = I.ErrScaleNonCanonical "XAU"
+type instance I.UnitScale "XAU" "troy-ounce" = '(1, 1)
+type instance I.UnitScale "XAU" "grain" = '(480, 1)
+type instance I.UnitScale "XAU" "milligrain" = '(480000, 1)
+type instance I.UnitScale "XAU" "micrograin" = '(480000000, 1)
+type instance I.UnitScale "XAU" "kilogram" = '(31103477, 1000000000)
+type instance I.UnitScale "XAU" "gram" = '(31103477, 1000000)
+type instance I.UnitScale "XAU" "milligram" = '(31103477, 1000)
+type instance I.UnitScale "XAU" "microgram" = '(31103477, 1)
+-- | Silver. No canonical smallest unit. Unusable instance.
+type instance I.CurrencyScale "XAG" = I.ErrScaleNonCanonical "XAU"
+type instance I.UnitScale "XAG" "troy-ounce" = '(1, 1)
+type instance I.UnitScale "XAG" "grain" = '(480, 1)
+type instance I.UnitScale "XAG" "milligrain" = '(480000, 1)
+type instance I.UnitScale "XAG" "micrograin" = '(480000000, 1)
+type instance I.UnitScale "XAG" "kilogram" = '(31103477, 1000000000)
+type instance I.UnitScale "XAG" "gram" = '(31103477, 1000000)
+type instance I.UnitScale "XAG" "milligram" = '(31103477, 1000)
+type instance I.UnitScale "XAG" "microgram" = '(31103477, 1)
+-- | Palladium. No canonical smallest unit. Unusable instance.
+type instance I.CurrencyScale "XPD" = I.ErrScaleNonCanonical "XPD"
+type instance I.UnitScale "XPD" "troy-ounce" = '(1, 1)
+type instance I.UnitScale "XPD" "grain" = '(480, 1)
+type instance I.UnitScale "XPD" "milligrain" = '(480000, 1)
+type instance I.UnitScale "XPD" "micrograin" = '(480000000, 1)
+type instance I.UnitScale "XPD" "kilogram" = '(31103477, 1000000000)
+type instance I.UnitScale "XPD" "gram" = '(31103477, 1000000)
+type instance I.UnitScale "XPD" "milligram" = '(31103477, 1000)
+type instance I.UnitScale "XPD" "microgram" = '(31103477, 1)
+-- | Platinum. No canonical smallest unit. Unusable instance.
+type instance I.CurrencyScale "XPT" = I.ErrScaleNonCanonical "XPT"
+type instance I.UnitScale "XPT" "troy-ounce" = '(1, 1)
+type instance I.UnitScale "XPT" "grain" = '(480, 1)
+type instance I.UnitScale "XPT" "milligrain" = '(480000, 1)
+type instance I.UnitScale "XPT" "micrograin" = '(480000000, 1)
+type instance I.UnitScale "XPT" "kilogram" = '(31103477, 1000000000)
+type instance I.UnitScale "XPT" "gram" = '(31103477, 1000000)
+type instance I.UnitScale "XPT" "milligram" = '(31103477, 1000)
+type instance I.UnitScale "XPT" "microgram" = '(31103477, 1)
+-- | Bitcoin
+type instance I.CurrencyScale "BTC" = I.UnitScale "BTC" "satoshi"
+type instance I.UnitScale "BTC" "bitcoin" = '(1, 1)
+type instance I.UnitScale "BTC" "millibitcoin" = '(1000, 1)
+type instance I.UnitScale "BTC" "satoshi" = '(100000000, 1)
+-- | Bitcoin
+type instance I.CurrencyScale "XBT" = I.CurrencyScale "BTC"
+type instance I.UnitScale "XBT" "bitcoin" = I.UnitScale "BTC" "bitcoin"
+type instance I.UnitScale "XBT" "millibitcoin" = I.UnitScale "BTC" "millibitcoin"
+type instance I.UnitScale "XBT" "satoshi" = I.UnitScale "BTC" "satoshi"
+-- | Ether
+type instance I.CurrencyScale "ETH" = I.UnitScale "ETH" "wei"
+type instance I.UnitScale "ETH" "ether"      = '(1, 1)
+type instance I.UnitScale "ETH" "kwei"       = '(1000, 1)
+type instance I.UnitScale "ETH" "babbage"    = '(1000, 1)
+type instance I.UnitScale "ETH" "mwei"       = '(1000000, 1)
+type instance I.UnitScale "ETH" "lovelace"   = '(1000000, 1)
+type instance I.UnitScale "ETH" "gwei"       = '(1000000000, 1)
+type instance I.UnitScale "ETH" "shannon"    = '(1000000000, 1)
+type instance I.UnitScale "ETH" "microether" = '(1000000000000, 1)
+type instance I.UnitScale "ETH" "szabo"      = '(1000000000000, 1)
+type instance I.UnitScale "ETH" "finney"     = '(1000000000000000, 1)
+type instance I.UnitScale "ETH" "milliether" = '(1000000000000000, 1)
+type instance I.UnitScale "ETH" "wei"        = '(1000000000000000000, 1)
+-- | Cardano
+type instance I.CurrencyScale "ADA" = I.UnitScale "ADA" "lovelace"
+type instance I.UnitScale "ADA" "ada" = '(1, 1)
+type instance I.UnitScale "ADA" "lovelace" = '(1000000, 1)
+-- | Litecoin
+type instance I.CurrencyScale "LTC" = I.UnitScale "LTC" "photon"
+type instance I.UnitScale "LTC" "litecoin" = '(1, 1)
+type instance I.UnitScale "LTC" "lite" = '(1000, 1)
+type instance I.UnitScale "LTC" "photon" = '(100000000, 1)
+-- | Ripple
+type instance I.CurrencyScale "XRP" = I.UnitScale "XRP" "drop"
+type instance I.UnitScale "XRP" "ripple" = '(1, 1)
+type instance I.UnitScale "XRP" "drop" = '(1000000, 1)
+-- | Monero
+type instance I.CurrencyScale "XMR" = I.UnitScale "XMR" "piconero"
+type instance I.UnitScale "XMR" "monero" = '(1, 1)
+type instance I.UnitScale "XMR" "decinero" = '(10, 1)
+type instance I.UnitScale "XMR" "centinero" = '(100, 1)
+type instance I.UnitScale "XMR" "millinero" = '(1000, 1)
+type instance I.UnitScale "XMR" "micronero" = '(1000000, 1)
+type instance I.UnitScale "XMR" "nanonero" = '(1000000000, 1)
+type instance I.UnitScale "XMR" "piconero" = '(1000000000000, 1)
 
diff --git a/src/Money/Internal.hs b/src/Money/Internal.hs
--- a/src/Money/Internal.hs
+++ b/src/Money/Internal.hs
@@ -43,9 +43,13 @@
  , discreteToDecimal
    -- * Currency scales
  , Scale
+ , scaleFromRational
+ , scaleToRational
+ , scale
+ , UnitScale
+ , CurrencyScale
  , GoodScale
  , ErrScaleNonCanonical
- , scale
    -- * Currency exchange
  , ExchangeRate
  , exchangeRate
@@ -85,10 +89,30 @@
  , someExchangeRateDstCurrency
  , someExchangeRateDstCurrency'
  , someExchangeRateRate
- -- * Misc
- , Approximation(Round, Floor, Ceiling, Truncate)
+ -- * Rationals
  , rationalToDecimal
  , rationalFromDecimal
+ -- * Miscellaneous
+ , Approximation(Round, Floor, Ceiling, Truncate, HalfEven)
+ , approximate
+ -- ** Decimal config
+ , DecimalConf(..)
+ , defaultDecimalConf
+ -- *** Separators
+ , Separators
+ , mkSeparators
+ , separatorsComma
+ , separatorsCommaDot
+ , separatorsCommaNarrownbsp
+ , separatorsCommaNbsp
+ , separatorsCommaThinsp
+ , separatorsCommaSpace
+ , separatorsDot
+ , separatorsDotComma
+ , separatorsDotNarrownbsp
+ , separatorsDotThinsp
+ , separatorsDotNbsp
+ , separatorsDotSpace
  ) where
 
 import Control.Applicative ((<|>), empty)
@@ -300,8 +324,8 @@
 --
 -- For example, if you want to represent @GBP 21.05@, where the smallest
 -- represetable unit for a GBP (United Kingdom Pound) is the /penny/, and 100
--- /pennies/ equal 1 GBP (i.e., @'Scale' \"GBP\" ~ '(100, 1)@), then you can
--- use:
+-- /pennies/ equal 1 GBP (i.e., @'UnitScale' \"GBP\" \"penny\" ~ '(100, 1)@), then
+-- you can use:
 --
 -- @
 -- 'discrete' 2105 :: 'Discrete' \"GBP\" \"penny\"
@@ -309,7 +333,7 @@
 --
 -- Because @2015 / 100 == 20.15@.
 type Discrete (currency :: Symbol) (unit :: Symbol)
-  = Discrete' currency (Scale currency unit)
+  = Discrete' currency (UnitScale currency unit)
 
 -- | 'Discrete'' represents a discrete monetary value for a @currency@ expresed
 -- as amount of @scale@, which is a rational number expressed as @(numerator,
@@ -373,24 +397,24 @@
   ( KnownSymbol currency, GoodScale scale
   ) => Show (Discrete' currency scale) where
   showsPrec n = \d0@(Discrete i0) ->
-    let c = symbolVal (Proxy :: Proxy currency)
-        s = scale d0
+    let c = symbolVal (Proxy :: Proxy currency) :: String
+        rs = scaleToRational (scale d0) :: Rational
     in showParen (n > 10) $
          showString "Discrete " .  showsPrec 0 c . showChar ' ' .
-         showsPrec 0 (numerator s) . showChar '%' .
-         showsPrec 0 (denominator s) . showChar ' ' .
+         showsPrec 0 (numerator rs) . showChar '%' .
+         showsPrec 0 (denominator rs) . showChar ' ' .
          showsPrec 0 i0
 
 instance forall currency scale.
   ( KnownSymbol currency, GoodScale scale
   ) => Read (Discrete' currency scale) where
   readPrec = Read.parens $ do
-    let c = symbolVal (Proxy :: Proxy currency)
-        s = scale (Proxy :: Proxy scale)
+    let c = symbolVal (Proxy :: Proxy currency) :: String
+        rs = scaleToRational (scale (Proxy :: Proxy scale)) :: Rational
     _ <- ReadPrec.lift (ReadP.string (concat
            [ "Discrete ", show c, " "
-           , show (numerator s), "%"
-           , show (denominator s), " "
+           , show (numerator rs), "%"
+           , show (denominator rs), " "
            ]))
     fmap Discrete Read.readPrec
 
@@ -428,7 +452,8 @@
   :: GoodScale scale
   => Discrete' currency scale
   -> Dense currency -- ^
-denseFromDiscrete = \c@(Discrete i) -> Dense (fromInteger i / scale c)
+denseFromDiscrete = \c@(Discrete i) ->
+  Dense (fromInteger i / scaleToRational (scale c))
 {-# INLINE denseFromDiscrete #-}
 
 -- | 'Discrete' currency identifier.
@@ -464,9 +489,11 @@
   -- ^ Approximate @x@ to the nearest integer greater than or equal to @x@.
   | Truncate
   -- ^ Approximate @x@ to the nearest integer betwen @0@ and @x@, inclusive.
+  | HalfEven
+  -- ^ Approximate @x@ to the nearest even integer, when equidistant from the
+  -- nearest two integers. This is also known as “Bankers Rounding”.
   deriving (Eq, Ord, Show, Read, GHC.Generic)
 
-
 approximate :: Approximation -> Rational -> Integer
 {-# INLINE approximate #-}
 approximate = \case
@@ -474,7 +501,20 @@
   Floor -> floor
   Ceiling -> ceiling
   Truncate -> truncate
+  HalfEven -> halfEven
 
+-- | Approximate to the nearest even integer, when equidistant from the nearest
+-- two integers. This is also known as “Bankers Rounding”.
+halfEven :: Rational -> Integer
+{-# INLINABLE halfEven #-}
+halfEven = \r ->                          --    1.5    -1.5
+  let tr  :: Integer  = truncate r        --    1.0    -1.0
+      rr  :: Rational = toRational tr - r --   -0.5     0.5
+  in if | abs rr /= 1%2 -> round r
+        | even tr -> tr
+        | otherwise -> tr + signum tr
+
+
 -- | Approximate a 'Dense' value @x@ to the nearest value fully representable a
 -- given @scale@.
 --
@@ -497,7 +537,7 @@
   -> (Discrete' currency scale, Dense currency)
 discreteFromDense a = \c0 ->
   let !r0 = toRational c0 :: Rational
-      !r1 = scale (Proxy :: Proxy scale)
+      !r1 = scaleToRational (scale (Proxy :: Proxy scale)) :: Rational
       !i2 = approximate a (r0 * r1) :: Integer
       !r2 = fromInteger i2 / r1 :: Rational
       !d2 = Discrete i2
@@ -507,42 +547,55 @@
 
 --------------------------------------------------------------------------------
 
--- | @'Scale' currency unit@ is an rational number (expressed as @'(numerator,
+-- | This is the term-level representation of the “scale” we represent as
+-- @('Nat', 'Nat')@ elsewhere in the type system (e.g., in 'GoodScale' or
+-- 'UnitScale').
+--
+-- See 'UnitScale' for a detailed description.
+newtype Scale = Scale Rational
+  deriving (Eq, Ord, Show, GHC.Generic)
+
+-- | Construct a 'Scale' from a positive, non-zero rational number.
+scaleFromRational :: Rational -> Maybe Scale
+{-# INLINE scaleFromRational #-}
+scaleFromRational r = do
+  guard (denominator r /= 0 && r > 0)
+  Just (Scale (abs (numerator r) % abs (denominator r)))
+
+-- | Obtain the 'Rational' representation of a 'Scale'.
+scaleToRational :: Scale -> Rational
+{-# INLINE scaleToRational #-}
+scaleToRational (Scale r) = r
+
+-- | @'UnitScale' currency unit@ is an rational number (expressed as @'(numerator,
 -- denominator)@) indicating how many pieces of @unit@ fit in @currency@.
 --
 -- @currency@ is usually a ISO-4217 currency code, but not necessarily.
 --
--- The 'Scale' will determine how to convert a 'Dense' value into a
--- 'Discrete' value and vice-versa.
+-- The resulting @('Nat', 'Nat')@, which is the type-level representation for
+-- what at the term-level we call 'Scale', will determine how to convert a
+-- 'Dense' value into a 'Discrete' value and vice-versa.
 --
 -- For example, there are 100 USD cents in 1 USD, so the scale for this
 -- relationship is:
 --
 -- @
--- type instance 'Scale' \"USD\" \"cent\" = '(100, 1)
+-- type instance 'UnitScale' \"USD\" \"cent\" = '(100, 1)
 -- @
 --
 -- As another example, there is 1 dollar in USD, so the scale for this
 -- relationship is:
 --
 -- @
--- type instance 'Scale' \"USD\" \"dollar\" = '(1, 1)
+-- type instance 'UnitScale' \"USD\" \"dollar\" = '(1, 1)
 -- @
 --
 -- When using 'Discrete' values to represent money, it will be impossible to
 -- represent an amount of @currency@ smaller than @unit@. So, if you decide to
--- use @Scale \"USD\" \"dollar\"@ as your scale, you will not be able to
+-- use @UnitScale \"USD\" \"dollar\"@ as your scale, you will not be able to
 -- represent values such as USD 3.50 or USD 21.87 becacuse they are not exact
 -- multiples of a dollar.
 --
--- If there exists a canonical smallest @unit@ that can fully represent the
--- currency in all its denominations, then an instance @'Scale' currency
--- currency@ exists.
---
--- @
--- type instance 'Scale' \"USD\" \"USD\" = 'Scale' \"USD\" \"cent\"
--- @
---
 -- For some monetary values, such as precious metals, there is no smallest
 -- representable unit, since you can repeatedly split the precious metal many
 -- times before it stops being a precious metal. Nevertheless, for practical
@@ -554,7 +607,7 @@
 -- /troy ounce/ equals 480000 /milligrains/.
 --
 -- @
--- type instance 'Scale' \"XAU\" \"milligrain\" = '(480000, 1)
+-- type instance 'UnitScale' \"XAU\" \"milligrain\" = '(480000, 1)
 -- @
 --
 -- You can use other units such as /milligrams/ for measuring XAU, for example.
@@ -563,13 +616,28 @@
 -- express it.
 --
 -- @
--- type instance 'Scale' \"XAU\" \"milligram\" = '(31103477, 1000)
+-- type instance 'UnitScale' \"XAU\" \"milligram\" = '(31103477, 1000)
 -- @
 --
--- If you try to obtain the 'Scale' of a @currency@ without an obvious smallest
+-- If you try to obtain the 'UnitScale' of a @currency@ without an obvious smallest
 -- representable @unit@, like XAU, you will get a compile error.
-type family Scale (currency :: Symbol) (unit :: Symbol) :: (Nat, Nat)
+type family UnitScale (currency :: Symbol) (unit :: Symbol) :: (Nat, Nat)
 
+-- | If there exists a canonical smallest 'Scale' that can fully represent the
+-- @currency@ in all its denominations, then @'CurrencyScale' currency@ will
+-- return such 'Scale'. For example, @'CurrencyScale' \"USD\"@ evaluates to
+-- @'UnitScale' \"USD\" \"cent\"@.
+--
+-- @
+-- type instance 'CurrencyScale' \"USD\" = 'UnitScale' \"USD\" \"cent\"
+-- @
+--
+-- If the @currency@ doesn't have a canonical smallest 'Scale', then
+-- @'CurrencyScale' currency@ shall be left undefined or fail to compile with a
+-- 'GHC.TypeError'. For example @'CurrencyScale' \"XAU\"@ fails with
+-- @'ErrScaleNonCanonical' \"XAU\"@.
+type family CurrencyScale (currency :: Symbol) :: (Nat, Nat)
+
 -- | A friendly 'GHC.TypeError' to use for a @currency@ that doesn't have a
 -- canonical small unit.
 type family ErrScaleNonCanonical (currency :: Symbol) :: k where
@@ -578,7 +646,7 @@
       'GHC.Text " is not a currency with a canonical smallest unit," 'GHC.:$$:
       'GHC.Text "be explicit about the currency unit you want to use." )
 
--- | Constraints to a scale (like the one returned by @'Scale' currency unit@)
+-- | Constraints to a scale (like the one returned by @'UnitScale' currency unit@)
 -- expected to always be satisfied. In particular, the scale is always
 -- guaranteed to be a positive rational number ('GHC.Real.infinity' and
 -- 'GHC.Real.notANumber' are forbidden by 'GoodScale').
@@ -606,22 +674,28 @@
 
 -- | Term-level representation of a currrency @scale@.
 --
--- For example, the 'Scale' for @\"USD\"@ in @\"cent\"@s is @100/1@.
+-- For example, the 'Scale' for @\"USD\"@ in @\"cent\"@s is @100/1@. We can
+-- obtain a term-level representation for it using any of the following:
 --
 -- @
--- > 'scale' ('Proxy' :: 'Proxy' ('Scale' \"USD\" \"cent\"))
--- 100 '%' 1
+-- > 'scale' ('Proxy' :: 'Proxy' ('UnitScale' \"USD\" \"cent\"))
+-- 'Scale' (100 '%' 1)
 -- @
 --
 -- @
+-- > 'scale' ('Proxy' :: 'CurrencyScale' \"USD\")
+-- 'Scale' (100 '%' 1)
+-- @
+--
+-- @
 -- > 'scale' (x :: 'Discrete' \"USD\" \"cent\")
--- 100 '%' 1
+-- 'Scale' (100 '%' 1)
 -- @
 --
 -- The returned 'Rational' is statically guaranteed to be a positive number.
-scale :: forall proxy scale. GoodScale scale => proxy scale -> Rational -- ^
-scale = \_ -> natVal (Proxy :: Proxy (Fst scale)) %
-              natVal (Proxy :: Proxy (Snd scale))
+scale :: forall proxy scale. GoodScale scale => proxy scale -> Scale -- ^
+scale = \_ -> Scale (natVal (Proxy :: Proxy (Fst scale)) %
+                     natVal (Proxy :: Proxy (Snd scale)))
 {-# INLINE scale #-}
 
 --------------------------------------------------------------------------------
@@ -710,6 +784,8 @@
 {-# INLINE exchangeRateToRational #-}
 
 -- | Safely construct an 'ExchangeRate' from a *positive* 'Rational' number.
+--
+-- If the given 'Rational' is non-positive, returns 'Nothing'.
 exchangeRate :: Rational -> Maybe (ExchangeRate src dst)
 exchangeRate = \r ->
   if denominator r /= 0 && r > 0
@@ -772,9 +848,9 @@
   , _someDenseAmount            :: !Rational
   } deriving (Eq, Show, GHC.Generic)
 
--- | __WARNING__ This instance does not compare monetary amounts, it just helps
--- you sort 'SomeDense' values in case you need to put them in a 'Data.Set.Set'
--- or similar.
+-- | __WARNING__ This instance does not compare monetary amounts across
+-- different currencies, it just helps you sort 'SomeDense' values in case you
+-- need to put them in a 'Data.Set.Set' or similar.
 deriving instance Ord SomeDense
 
 -- | Currency name.
@@ -799,7 +875,7 @@
 -- operation on the monetary value.
 mkSomeDense
   :: T.Text   -- ^ Currency. ('someDenseCurrency')
-  -> Rational -- ^ Scale. ('someDenseAmount')
+  -> Rational -- ^ Amount. ('someDenseAmount')
   -> Maybe SomeDense
 {-# INLINE mkSomeDense #-}
 mkSomeDense = \c r -> mkSomeDense' (T.unpack c) r
@@ -870,13 +946,13 @@
     -- us to derive serialization instances maintaining backwards compatiblity
     -- with pre-0.6 versions of this library, when 'String' was the prefered
     -- string type, and not 'T.Text'.
-  , _someDiscreteScale    :: !Rational -- ^ Positive, non-zero.
+  , _someDiscreteScale    :: !Scale
   , _someDiscreteAmount   :: !Integer  -- ^ Amount of unit.
   } deriving (Eq, Show, GHC.Generic)
 
--- | __WARNING__ This instance does not compare monetary amounts, it just helps
--- you sort 'SomeDiscrete' values in case you need to put them in a
--- 'Data.Set.Set' or similar.
+-- | __WARNING__ This instance does not compare monetary amounts across
+-- different currencies, it just helps you sort 'SomeDiscrete' values in case
+-- you need to put them in a 'Data.Set.Set' or similar.
 deriving instance Ord SomeDiscrete
 
 -- | Currency name.
@@ -890,7 +966,7 @@
 {-# INLINE someDiscreteCurrency' #-}
 
 -- | Positive, non-zero.
-someDiscreteScale :: SomeDiscrete -> Rational
+someDiscreteScale :: SomeDiscrete -> Scale
 someDiscreteScale = _someDiscreteScale
 {-# INLINE someDiscreteScale #-}
 
@@ -902,23 +978,20 @@
 -- | Internal. Build a 'SomeDiscrete' from raw values.
 --
 -- This function is intended for deserialization purposes. You need to convert
--- this 'SomeDiscrete' value to a 'Discrete' vallue in order to do any arithmetic
--- operation on the monetary value.
+-- this 'SomeDiscrete' value to a 'Discrete' vallue in order to do any
+-- arithmetic operation on the monetary value.
 mkSomeDiscrete
   :: T.Text   -- ^ Currency name. ('someDiscreteCurrency')
-  -> Rational -- ^ Scale. Positive, non-zero. ('someDiscreteScale')
+  -> Scale    -- ^ Scale. Positive, non-zero. ('someDiscreteScale')
   -> Integer  -- ^ Amount of unit. ('someDiscreteAmount')
-  -> Maybe SomeDiscrete
+  -> SomeDiscrete
 {-# INLINE mkSomeDiscrete #-}
-mkSomeDiscrete = \c r a -> mkSomeDiscrete' (T.unpack c) r a
+mkSomeDiscrete = \c s a -> mkSomeDiscrete' (T.unpack c) s a
 
 -- | Like 'mkSomeDiscrete' but takes 'String' rather than 'T.Text'.
-mkSomeDiscrete' :: String -> Rational -> Integer -> Maybe SomeDiscrete
+mkSomeDiscrete' :: String -> Scale -> Integer -> SomeDiscrete
 {-# INLINABLE mkSomeDiscrete' #-}
-mkSomeDiscrete' = \c r a ->
-  if (denominator r /= 0) && (r > 0)
-  then Just (SomeDiscrete c r a)
-  else Nothing
+mkSomeDiscrete' = SomeDiscrete
 
 -- | Convert a 'Discrete' to a 'SomeDiscrete' for ease of serialization.
 toSomeDiscrete
@@ -929,7 +1002,7 @@
   let c = symbolVal (Proxy :: Proxy currency)
       n = natVal (Proxy :: Proxy (Fst scale))
       d = natVal (Proxy :: Proxy (Snd scale))
-  in SomeDiscrete c (n % d) i0
+  in SomeDiscrete c (Scale (n % d)) i0
 {-# INLINABLE toSomeDiscrete #-}
 
 -- | Attempt to convert a 'SomeDiscrete' to a 'Discrete', provided you know the
@@ -965,19 +1038,20 @@
            -> r )
   -> r  -- ^
 withSomeDiscrete dr = \f ->
-  case someSymbolVal (_someDiscreteCurrency dr) of
-    SomeSymbol (Proxy :: Proxy currency) ->
-      case someNatVal (numerator (someDiscreteScale dr)) of
-        Nothing -> error "withSomeDiscrete: impossible: numerator < 0"
-        Just (SomeNat (Proxy :: Proxy num)) ->
-          case someNatVal (denominator (someDiscreteScale dr)) of
-            Nothing -> error "withSomeDiscrete: impossible: denominator < 0"
-            Just (SomeNat (Proxy :: Proxy den)) ->
-              case mkGoodScale of
-                Nothing -> error "withSomeDiscrete: impossible: mkGoodScale"
-                Just (Dict :: Dict (GoodScale '(num, den))) ->
-                  f (Discrete (someDiscreteAmount dr)
-                       :: Discrete' currency '(num, den))
+  let rscale :: Rational = scaleToRational (someDiscreteScale dr)
+  in case someSymbolVal (_someDiscreteCurrency dr) of
+       SomeSymbol (Proxy :: Proxy currency) ->
+         case someNatVal (numerator rscale) of
+           Nothing -> error "withSomeDiscrete: impossible: numerator < 0"
+           Just (SomeNat (Proxy :: Proxy num)) ->
+             case someNatVal (denominator rscale) of
+               Nothing -> error "withSomeDiscrete: impossible: denominator < 0"
+               Just (SomeNat (Proxy :: Proxy den)) ->
+                 case mkGoodScale of
+                   Nothing -> error "withSomeDiscrete: impossible: mkGoodScale"
+                   Just (Dict :: Dict (GoodScale '(num, den))) ->
+                     f (Discrete (someDiscreteAmount dr)
+                          :: Discrete' currency '(num, den))
 {-# INLINABLE withSomeDiscrete #-}
 
 --------------------------------------------------------------------------------
@@ -1011,9 +1085,9 @@
   , _someExchangeRateRate            :: !Rational -- ^ Positive, non-zero.
   } deriving (Eq, Show, GHC.Generic)
 
--- | __WARNING__ This instance does not compare monetary amounts, it just helps
--- you sort 'SomeExchangeRate' values in case you need to put them in a
--- 'Data.Set.Set' or similar.
+-- | __WARNING__ This instance does not compare rates across different currency
+-- pairs (whatever that means), it just helps you sort 'SomeExchangeRate' values
+-- in case you need to put them in a 'Data.Set.Set' or similar.
 deriving instance Ord SomeExchangeRate
 
 -- | Source currency name.
@@ -1162,6 +1236,7 @@
 instance Hashable SomeDiscrete
 instance Hashable (ExchangeRate src dst)
 instance Hashable SomeExchangeRate
+instance Hashable Scale
 
 --------------------------------------------------------------------------------
 -- Extra instances: deepseq
@@ -1172,6 +1247,7 @@
 instance NFData SomeDiscrete
 instance NFData (ExchangeRate src dst)
 instance NFData SomeExchangeRate
+instance NFData Scale
 
 --------------------------------------------------------------------------------
 -- Extra instances: binary
@@ -1208,21 +1284,29 @@
     when (d == 0) (fail "denominator is zero")
     pure (mkSomeDense' c (n % d))
 
+instance Binary.Binary Scale where
+  put = \s ->
+    let r = scaleToRational s
+    in Binary.put (numerator r) <>
+       Binary.put (denominator r)
+  get = maybe empty pure =<< do
+    n :: Integer <- Binary.get
+    d :: Integer <- Binary.get
+    when (d == 0) (fail "denominator is zero")
+    pure (scaleFromRational (n % d))
+
 -- | Compatible with 'Discrete'.
 instance Binary.Binary SomeDiscrete where
-  put = \(SomeDiscrete c r a) ->
+  put = \(SomeDiscrete c s a) ->
     -- We go through String for backwards compatibility.
     Binary.put c <>
-    Binary.put (numerator r) <>
-    Binary.put (denominator r) <>
+    Binary.put s <>
     Binary.put a
-  get = maybe empty pure =<< do
+  get = do
     c :: String <- Binary.get
-    n :: Integer <- Binary.get
-    d :: Integer <- Binary.get
-    when (d == 0) (fail "denominator is zero")
+    s :: Scale <- Binary.get
     a :: Integer <- Binary.get
-    pure (mkSomeDiscrete' c (n % d) a)
+    pure (mkSomeDiscrete' c s a)
 
 -- | Compatible with 'ExchangeRate'.
 instance Binary.Binary SomeExchangeRate where
@@ -1246,228 +1330,261 @@
 -- manner.
 --
 -- @
--- > 'denseToDecimal' 'Round' 'True' ('Just' \',\') \'.\' 2 (1 '%' 1)
---      ('dense'' (123456 '%' 100) :: 'Dense' \"USD\")
--- Just \"+1,234.56\"
--- @
---
--- @
--- > 'denseToDecimal' 'Round' 'True' ('Just' \',\') \'.\' 2 (100 '%' 1)
+-- > 'denseToDecimal' 'defaultDecimalConf' 'Round'
 --      ('dense'' (123456 '%' 100) :: 'Dense' \"USD\")
--- Just \"+123,456.00\"
+-- \"1234.56\"
 -- @
---
--- This function returns 'Nothing' if the scale is less than @1@, or if it's not
--- possible to reliably render the decimal string due to a bad choice of
--- separators. That is, if the separators are digits or equal among themselves,
--- this function returns 'Nothing'.
 denseToDecimal
-  :: Approximation
+  :: DecimalConf
+  -- ^ Config to use for rendering the decimal number.
+  -> Approximation
   -- ^ Approximation to use if necesary in order to fit the 'Dense' amount in
   -- as many decimal numbers as requested.
-  -> Bool
-  -- ^ Whether to render a leading @\'+\'@ sign in case the amount is positive.
-  -> Maybe Char
-  -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in
-  -- @1,234.56789@).
-  --
-  -- If the given separator is a digit, or if it is equal to the decimal
-  -- separator, then this functions returns 'Nothing'.
-  -> Char
-  -- ^ Decimal separator (i.e., the @\'.\'@ in @1,234.56789@).
-  --
-  -- If the given separator is a digit, or if it is equal to the thousands
-  -- separator, then this functions returns 'Nothing'.
-  -> Word8
-  -- ^ Number of decimal numbers to render, if any.
-  -> Rational
-  -- ^ Scale used to when rendering the decimal number. This is useful if you
-  -- want to render a “number of cents” rather than a “number of dollars” when
-  -- rendering a USD amount, for example.
-  --
-  -- Set this to @1 '%' 1@ if you don't care.
-  --
-  -- For example, when rendering render @'dense'' (123 '%' 100) :: 'Dense'
-  -- "USD"@ as a decimal number with three decimal places, a scale of @1 '%' 1@
-  -- (analogous to  @'Scale' \"USD\" \"dollar\"@) would render @1@ as the
-  -- integer part and @230@ as the fractional part, whereas a scale of @100 '%'
-  -- 1@ (analogous @'Scale' \"USD\" \"cent\"@) would render @123@ as the integer
-  -- part and @000@ as the fractional part.
-  --
-  -- You can easily obtain the scale for a particular currency and unit
-  -- combination using the 'scale' function. Otherwise, you are free to pass in
-  -- any other /positive/ 'Rational' number. If a non-positive scale is given,
-  -- then this function returns 'Nothing'.
-  --
-  -- Specifying scales other than @1 '%' 1@ is particularly useful for
-  -- currencies whose main unit is too big. For example, the main unit of gold
-  -- (XAU) is the troy-ounce, which is too big for day to day accounting, so
-  -- using the gram or the grain as the unit when rendering decimal amounts
-  -- could be useful.
-  --
-  -- Be careful when using a scale smaller than @1 '%' 1@, since it may become
-  -- impossible to parse back a meaningful amount from the rendered decimal
-  -- representation unless a big number of fractional digits is used.
   -> Dense currency
   -- ^ The dense monetary amount to render.
-  -> Maybe T.Text
+  -> T.Text
   -- ^ Returns 'Nothing' is the given separators are not acceptable (i.e., they
   -- are digits, or they are equal).
 {-# INLINABLE denseToDecimal #-}
-denseToDecimal a plus ytsep dsep fdigs scal = \(Dense r0) -> do
-  guard (scal > 0)
-  rationalToDecimal a plus ytsep dsep fdigs (r0 * scal)
+denseToDecimal ds a = \(Dense r0) -> do
+  rationalToDecimal ds a r0
 
+
 -- | Render a 'Discrete'' monetary amount as a decimal number in a potentially
 -- lossy manner.
 --
 -- This is simply a convenient wrapper around 'denseToDecimal':
 --
 -- @
--- 'discreteToDecimal' a b c d e f (dis :: 'Discrete'' currency scale)
---     == 'denseToDecimal' a b c d e f ('denseFromDiscrete' dis :: 'Dense' currency)
+-- 'discreteToDecimal' ds a (dis :: 'Discrete'' currency scale)
+--     == 'denseToDecimal' ds a ('denseFromDiscrete' dis :: 'Dense' currency)
 -- @
 --
 -- In particular, the @scale@ in @'Discrete'' currency scale@ has no influence
--- over the scale in which the decimal number is rendered. Use the 'Rational'
--- parameter to this function for modifying that behavior.
+-- over the scale in which the decimal number is rendered. Change the scale
+-- with 'decimalConf_scale' in order to modify that behavior.
 --
 -- Please refer to 'denseToDecimal' for further documentation.
---
--- This function returns 'Nothing' if the scale is less than @1@, or if it's not
--- possible to reliably render the decimal string due to a bad choice of
--- separators. That is, if the separators are digits or equal among themselves,
--- this function returns 'Nothing'.
 discreteToDecimal
   :: GoodScale scale
-  => Approximation
+  => DecimalConf
+  -- ^ Config to use for rendering the decimal number.
+  -> Approximation
   -- ^ Approximation to use if necesary in order to fit the 'Discrete' amount in
   -- as many decimal numbers as requested.
-  -> Bool
-  -- ^ Whether to render a leading @\'+\'@ sign in case the amount is positive.
-  -> Maybe Char
-  -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in
-  -- @1,234.56789@).
-  --
-  -- If the given separator is a digit, or if it is equal to the decimal
-  -- separator, then this functions returns 'Nothing'.
-  -> Char
-  -- ^ Decimal separator (i.e., the @\'.\'@ in @1,234.56789@).
-  --
-  -- If the given separator is a digit, or if it is equal to the thousands
-  -- separator, then this functions returns 'Nothing'.
-  -> Word8
-  -- ^ Number of decimal numbers to render, if any.
-  -> Rational
-  -- ^ Scale used to when rendering the decimal number. This is useful if you
-  -- want to render a “number of cents” rather than a “number of dollars” when
-  -- rendering a USD amount, for example.
-  --
-  -- Set this to @1 '%' 1@ if you don't care.
-  --
-  -- For example, when rendering render @'discrete' 123 :: 'Dense' \"USD\"
-  -- \"cent\"@ as a decimal number with three decimal places, a scale of @1 '%'
-  -- 1@ (analogous to  @'Scale' \"USD\" \"dollar\"@) would render @1@ as the
-  -- integer part and @230@ as the fractional part, whereas a scale of @100 '%'
-  -- 1@ (analogous @'Scale' \"USD\" \"cent\"@) would render @123@ as the integer
-  -- part and @000@ as the fractional part.
-  --
-  -- You can easily obtain the scale for a particular currency and unit
-  -- combination using the 'scale' function. Otherwise, you are free to pass in
-  -- any other /positive/ 'Rational' number. If a non-positive scale is
-  -- given, then this function returns 'Nothing'.
-  --
-  -- Specifying scales other than @1 '%' 1@ is particularly useful for
-  -- currencies whose main unit is too big. For example, the main unit of gold
-  -- (XAU) is the troy-ounce, which is too big for day to day accounting, so
-  -- using the gram or the grain as the unit when rendering decimal amounts
-  -- could be useful.
-  --
-  -- Be careful when using a scale smaller than @1 '%' 1@, since it may become
-  -- impossible to parse back a meaningful amount from the rendered decimal
-  -- representation unless a big number of fractional digits is used.
   -> Discrete' currency scale
   -- ^ The monetary amount to render.
-  -> Maybe T.Text
+  -> T.Text
   -- ^ Returns 'Nothing' is the given separators are not acceptable (i.e., they
   -- are digits, or they are equal).
 {-# INLINABLE discreteToDecimal #-}
-discreteToDecimal a plus ytsep dsep fdigs scal = \dns ->
-  denseToDecimal a plus ytsep dsep fdigs scal (denseFromDiscrete dns)
+discreteToDecimal ds a = \dns ->
+  denseToDecimal ds a (denseFromDiscrete dns)
 
 -- | Render a 'ExchangeRate' as a decimal number in a potentially lossy manner.
 --
 -- @
--- > 'exchangeRateToDecimal' 'Round' 'True' ('Just' \',\') \'.\' 2
---       '=<<' ('exchangeRate' (123456 '%' 100) :: 'Maybe' ('ExchangeRate' \"USD\" \"EUR\"))
+-- > 'exchangeRateToDecimal' 'defaultDecimalConf' 'Round'
+--       '<$>' ('exchangeRate' (123456 '%' 100) :: 'Maybe' ('ExchangeRate' \"USD\" \"EUR\"))
 -- Just \"1,234.56\"
 -- @
---
--- This function returns 'Nothing' if it is not possible to reliably render the
--- decimal string due to a bad choice of separators. That is, if the separators
--- are digits or equal among themselves, this function returns 'Nothing'.
 exchangeRateToDecimal
-  :: Approximation
+  :: DecimalConf
+  -- ^ Config to use for rendering the decimal number.
+  -> Approximation
   -- ^ Approximation to use if necesary in order to fit the 'Dense' amount in
   -- as many decimal numbers as requested.
-  -> Maybe Char
-  -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in
-  -- @1,234.56789@).
-  -> Char
-  -- ^ Decimal separator (i.e., the @\'.\'@ in @1,234.56789@)
-  -> Word8
-  -- ^ Number of decimal numbers to render, if any.
   -> ExchangeRate src dst
   -- ^ The 'ExchangeRate' to render.
-  -> Maybe T.Text
+  -> T.Text
   -- ^ Returns 'Nothing' if the given separators are not acceptable (i.e., they
   -- are digits, or they are equal).
 {-# INLINABLE exchangeRateToDecimal #-}
-exchangeRateToDecimal a ytsep dsep fdigs0 = \(ExchangeRate r0) ->
-  rationalToDecimal a False ytsep dsep fdigs0 r0
+exchangeRateToDecimal ds a = \(ExchangeRate r0) ->
+  rationalToDecimal ds a r0
 
--- | Render a 'Rational' number as a decimal approximation.
+--------------------------------------------------------------------------------
+
+-- | Decimal and thousands separators used when rendering or parsing a decimal
+-- number.
 --
--- This function returns 'Nothing' if it is not possible to reliably render the
--- decimal string due to a bad choice of separators. That is, if the separators
--- are digits or equal among themselves, this function returns 'Nothing'.
-rationalToDecimal
-  :: Approximation
-  -- ^ Approximation to use if necesary in order to fit the 'Dense' amount in
-  -- as many decimal numbers as requested.
-  -> Bool
-  -- ^ Whether to render a leading @\'+\'@ sign in case the amount is positive.
+-- Use 'mkSeparators' to construct.
+data Separators = Separators Char (Maybe Char)
+  deriving (Eq, Show)
+
+-- | Construct 'Separators' to use with in 'DecimalConf'.
+--
+-- The separatorsA can't be an ASCII digit nor control character, and they must
+-- be different from each other.
+mkSeparators
+  :: Char
+  -- ^ Decimal separator (i.e., the @\'.\'@ in @1,234.56789@)
   -> Maybe Char
   -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in
   -- @1,234.56789@).
-  -> Char
-  -- ^ Decimal separator (i.e., the @\'.\'@ in @1,234.56789@)
-  -> Word8
+  -> Maybe Separators
+mkSeparators ds yts = do
+  guard (not (Char.isDigit ds || Char.isControl ds))
+  for_ yts $ \ts ->
+    guard (not (ts == ds || Char.isDigit ts || Char.isControl ts))
+  pure (Separators ds yts)
+
+-- | @1234567,89@
+separatorsComma :: Separators
+separatorsComma = Separators ',' Nothing
+
+-- | @1.234.567,89@
+separatorsCommaDot :: Separators
+separatorsCommaDot = Separators ',' (Just '.')
+
+-- | @1 234 567,89@
+--
+-- The whitespace is Unicode's /NARROW NO-BREAK SPACE/ (U+202f, 8239,
+-- @'\8239'@).
+separatorsCommaNarrownbsp :: Separators
+separatorsCommaNarrownbsp = Separators ',' (Just '\8239')
+
+-- | @1 234 567,89@
+--
+-- The whitespace is Unicode's /NO-BREAK SPACE/ (U+00a0, 160, @'\160'@).
+separatorsCommaNbsp :: Separators
+separatorsCommaNbsp = Separators ',' (Just '\160')
+
+-- | @1 234 567,89@
+--
+-- The whitespace is Unicode's /THIN SPACE/ (U+2009, 8201, @'\8201'@).
+separatorsCommaThinsp :: Separators
+separatorsCommaThinsp = Separators ',' (Just '\8201')
+
+-- | @1 234 567,89@
+--
+-- The whitespace is ASCII's /SPC/ (U+0020, 32, @'\32'@).
+separatorsCommaSpace :: Separators
+separatorsCommaSpace = Separators ',' (Just '\32')
+
+-- | @1234567.89@
+separatorsDot :: Separators
+separatorsDot = Separators '.' Nothing
+
+-- | @1,234,567.89@
+separatorsDotComma :: Separators
+separatorsDotComma = Separators '.' (Just ',')
+
+-- | @1 234 567.89@
+--
+-- The whitespace is Unicode's /NARROW NO-BREAK SPACE/ (U+202f, 8239,
+-- @'\8239'@).
+separatorsDotNarrownbsp :: Separators
+separatorsDotNarrownbsp = Separators '.' (Just '\8239')
+
+-- | @1 234 567.89@
+--
+-- The whitespace is Unicode's /THIN SPACE/ (U+2009, 8201, @'\8201'@).
+separatorsDotThinsp :: Separators
+separatorsDotThinsp = Separators '.' (Just '\8201')
+
+-- | @1 234 567.89@
+--
+-- The whitespace is Unicode's /NO-BREAK SPACE/ (U+00a0, 160, @'\160'@).
+separatorsDotNbsp :: Separators
+separatorsDotNbsp = Separators '.' (Just '\160')
+
+-- | @1 234 567.89@
+--
+-- The whitespace is ASCII's /SPACE/ (U+0020, 32, @'\32'@).
+separatorsDotSpace :: Separators
+separatorsDotSpace = Separators '.' (Just '\32')
+
+--------------------------------------------------------------------------------
+
+-- | Config to use when rendering or parsing decimal numbers.
+--
+-- See 'defaultDecimalConf'.
+data DecimalConf = DecimalConf
+  { decimalConf_separators :: !Separators
+  -- ^ Decimal and thousands separators to use when rendering the decimal number.
+  -- Construct one with 'mkSeparators', or pick a ready made one like
+  -- 'separatorsDot' or 'separatorsDotNarrownbsp'.
+  , decimalConf_leadingPlus :: !Bool
+  -- ^ Whether to render a leading @\'+\'@ sign in case the amount is positive.
+  , decimalConf_digits :: !Word8
   -- ^ Number of decimal numbers to render, if any.
+  , decimalConf_scale :: !Scale
+  -- ^ Scale used to when rendering the decimal number. This is useful if, for
+  -- example, you want to render a “number of cents” rather than a “number of
+  -- dollars” as the whole part of the decimal number when rendering a USD
+  -- amount. It's particularly useful when rendering currencies such as XAU,
+  -- where one might prefer to render amounts as a number of grams, rather than
+  -- as a number of troy-ounces.
+  --
+  -- /Set this to @1@ if you don't care./
+  --
+  -- For example, when rendering render @'dense'' (123 '%' 100) :: 'Dense'
+  -- \"USD\"@ as a decimal number with two decimal places, a scale of @1@
+  -- (analogous to  @'UnitScale' \"USD\" \"dollar\"@) would render @1@ as the
+  -- integer part and @23@ as the fractional part, whereas a scale of @100@
+  -- (analogous @'UnitScale' \"USD\" \"cent\"@) would render @123@ as the integer
+  -- part and @00@ as the fractional part.
+  --
+  -- You can easily obtain the scale for a particular currency and unit
+  -- combination using the 'scale' function.
+  --
+  -- __Important:__ Generally, you will want this number to be @1@ or larger.
+  -- This is because scales in the range @(0, 1)@ can be particularly lossy unless
+  -- the number of decimal digits is sufficiently large.
+  } deriving (Eq, Show)
+
+-- | Default 'DecimalConf'.
+--
+-- * No leading @\'+\'@ sign
+--
+-- * No thousands separator
+--
+-- * Decimal separator is @\'.\'@
+--
+-- * @2@ decimal digits
+--
+-- * A scale of @1@
+--
+-- That is, something like @1.23@ or @-1234567.89@.
+defaultDecimalConf :: DecimalConf
+defaultDecimalConf = DecimalConf
+  { decimalConf_separators = separatorsDot
+  , decimalConf_leadingPlus = False
+  , decimalConf_digits = 2
+  , decimalConf_scale = Scale 1
+  }
+
+--------------------------------------------------------------------------------
+
+-- | Render a 'Rational' number as a decimal approximation.
+rationalToDecimal
+  :: DecimalConf
+  -- ^ Config to use for rendering the decimal number.
+  -> Approximation
+  -- ^ Approximation to use if necesary in order to fit the 'Dense' amount in
+  -- as many decimal numbers as requested.
   -> Rational
   -- ^ The dense monetary amount to render.
-  -> Maybe T.Text
+  -> T.Text
   -- ^ Returns 'Nothing' if the given separators are not acceptable (i.e., they
   -- are digits, or they are equal).
 {-# INLINABLE rationalToDecimal #-}
-rationalToDecimal a plus ytsep dsep fdigs0 = \r0 -> do
-  guard (not (Char.isDigit dsep))
-  for_ ytsep $ \tsep ->
-     guard (tsep /= dsep && not (Char.isDigit tsep))
+rationalToDecimal (DecimalConf (Separators ds yts) plus fdigs0 scal) a = \r0 -> do
   -- this string-fu is not particularly efficient.
-  let start = r0 * (10 ^ fdigs0) :: Rational
+  let start = r0 * scaleToRational scal * (10 ^ fdigs0) :: Rational
       parts = approximate a start :: Integer
       ipart = fromInteger (abs parts) `div` (10 ^ fdigs0) :: Natural
       ftext | ipart == 0 = show (abs parts) :: String
             | otherwise = drop (length (show ipart)) (show (abs parts))
-      itext = maybe (show ipart) (renderThousands ipart) ytsep :: String
+      itext = maybe (show ipart) (renderThousands ipart) yts :: String
       fpad0 = List.replicate (fromIntegral fdigs0 - length ftext) '0' :: String
-  Just $ T.pack $ mconcat
+  T.pack $ mconcat
     [ if | parts < 0 -> "-"
          | plus && parts > 0 -> "+"
          | otherwise -> ""
     , itext
-    , if | fdigs0 > 0 -> dsep : if start < 1
+    , if | fdigs0 > 0 -> ds : if start < 1
                                    then fpad0 <> ftext
                                    else ftext <> fpad0
          | otherwise -> ""
@@ -1497,114 +1614,87 @@
 -- Decimal parsing
 
 -- | Parses a decimal representation of a 'Dense'.
---
--- Leading @\'-\'@ and @\'+\'@ characters are considered.
 denseFromDecimal
-  :: Maybe Char
-  -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in
-  -- @-1,234.56789@).
-  -> Char
-  -- ^ Decimal separator (i.e., the @\'.\'@ in @-1,234.56789@)
-  -> Rational
-  -- ^ Scale used by the rendered decimal. It is important to get this number
-  -- correctly, otherwise the resulting 'Dense' amount will be wrong. Please
-  -- refer to the documentation for 'denseToDecimal' to understand the meaning
-  -- of this scale.
+  :: DecimalConf
+  -- ^ Config to use for parsing the decimal number.
   --
-  -- In summary, this scale will have a value of @1@ unless the decimal amount
-  -- represents a unit other than the main unit for the currency (e.g., cents
-  -- rather than dollars for USD, or grams rather than troy-ounces for XAU, or
-  -- millibitcoins rather than bitcoins for BTC).
+  -- Notice that a leading @\'-\'@ or @\'+\'@ will always be correctly
+  -- interpreted, notwithstanding what the “leading @\'+\'@” policy is on
+  -- the given 'DecimalConf'.
   -> T.Text
   -- ^ The raw string containing the decimal representation (e.g.,
   -- @"-1,234.56789"@).
   -> Maybe (Dense currency)
-denseFromDecimal yst sf scal str = do
-  guard (scal > 0)
-  r <- rationalFromDecimal yst sf str
-  pure (Dense $! (r / scal))
+denseFromDecimal ds t = do
+  r <- rationalFromDecimal ds t
+  pure (Dense $! r)
 
 -- | Parses a decimal representation of a 'Discrete'.
 --
--- Leading @\'-\'@ and @\'+\'@ characters are considered.
---
 -- Notice that parsing will fail unless the entire precision of the decimal
 -- number can be represented in the desired @scale@.
 discreteFromDecimal
   :: GoodScale scale
-  => Maybe Char
-  -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in
-  -- @-1,234.56789@).
-  -> Char
-  -- ^ Decimal separator (i.e., the @\'.\'@ in @-1,234.56789@)
-  -> Rational
-  -- ^ Scale used by the rendered decimal. It is important to get this number
-  -- correctly, otherwise the resulting 'Dense' amount will be wrong. Please
-  -- refer to the documentation for 'denseToDecimal' to understand the meaning
-  -- of this scale.
+  => DecimalConf
+  -- ^ Config to use for parsing the decimal number.
   --
-  -- In summary, this scale will have a value of @1@ unless the decimal amount
-  -- represents a unit other than the main unit for the currency (e.g., cents
-  -- rather than dollars for USD, or grams rather than troy-ounces for XAU, or
-  -- millibitcoins rather than bitcoins for BTC).
+  -- Notice that a leading @\'-\'@ or @\'+\'@ will always be correctly
+  -- interpreted, notwithstanding what the “leading @\'+\'@” policy is on
+  -- the given 'DecimalConf'.
   -> T.Text
   -- ^ The raw string containing the decimal representation (e.g.,
   -- @"-1,234.56789"@).
   -> Maybe (Discrete' currency scale)
-discreteFromDecimal yst sf scal = \str -> do
-  dns <- denseFromDecimal yst sf scal str
+discreteFromDecimal ds = \t -> do
+  dns <- denseFromDecimal ds t
   case discreteFromDense Truncate dns of
     (x, 0) -> Just x
     _ -> Nothing -- We fail for decimals that don't fit exactly in our scale.
 
 -- | Parses a decimal representation of an 'ExchangeRate'.
 exchangeRateFromDecimal
-  :: Maybe Char
-  -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in
-  -- @1,234.56789@).
-  -> Char
-  -- ^ Decimal separator (i.e., the @\'.\'@ in @1,234.56789@)
+  :: DecimalConf
+  -- ^ Config to use for parsing the decimal number.
+  --
+  -- Notice that a leading @\'-\'@ or @\'+\'@ will always be correctly
+  -- interpreted, notwithstanding what the “leading @\'+\'@” policy is on
+  -- the given 'DecimalConf'.
   -> T.Text
   -- ^ The raw string containing the decimal representation (e.g.,
   -- @"1,234.56789"@).
   -> Maybe (ExchangeRate src dst)
-exchangeRateFromDecimal yst sf t
+exchangeRateFromDecimal ds t
   | T.isPrefixOf "-" t = Nothing
-  | otherwise = exchangeRate =<< rationalFromDecimal yst sf t
+  | otherwise = exchangeRate =<< rationalFromDecimal ds t
 
+-- | Parses a decimal number representation as 'T.Text' into a 'Rational'.
 rationalFromDecimal
-  :: Maybe Char
-  -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in
-  -- @-1,234.56789@).
-  -> Char
-  -- ^ Decimal separator (i.e., the @\'.\'@ in @-1,234.56789@)
+  :: DecimalConf
+  -- ^ Config to use for parsing the decimal number.
+  --
+  -- Notice that a leading @\'-\'@ or @\'+\'@ will always be correctly
+  -- interpreted, notwithstanding what the “leading @\'+\'@” policy is on
+  -- the given 'DecimalConf'.
   -> T.Text
   -- ^ The raw string containing the decimal representation (e.g.,
   -- @"-1,234.56789"@).
   -> Maybe Rational
-rationalFromDecimal yst sf = \t ->
-  case ReadP.readP_to_S (rationalFromDecimalP yst sf) (T.unpack t) of
+rationalFromDecimal ds = \t ->
+  case ReadP.readP_to_S (rationalFromDecimalP ds) (T.unpack t) of
     [(x,"")] -> Just x
     _ -> Nothing
 
 -- TODO limit number of digits parsed to prevent DoS
 rationalFromDecimalP
-  :: Maybe Char
-  -- ^ Thousands separator for the integer part, if any (i.e., the @\',\'@ in
-  -- @-1,234.56789@).
-  --
-  -- The separator can't be a digit or control character. If it is, then parsing
-  -- will always fail.
-  -> Char
-  -- ^ Decimal separator (i.e., the @\'.\'@ in @-1,234.56789@).
+  :: DecimalConf
+  -- ^ Config to use for parsing the decimal number.
   --
-  -- The separator can't be a digit or control character. If it is, then parsing
-  -- will always fail.
+  -- Notice that a leading @\'-\'@ or @\'+\'@ will always be correctly
+  -- interpreted, notwithstanding what the “leading @\'+\'@” policy is on
+  -- the given 'DecimalConf'.
   -> ReadP.ReadP Rational
-rationalFromDecimalP ytsep dsep = do
-   for_ ytsep $ \tsep ->
-      guard (tsep /= dsep && not (Char.isDigit tsep))
-   guard (not (Char.isDigit dsep))
+rationalFromDecimalP ds = do
+   let Separators dsep ytsep = decimalConf_separators ds
    sig :: Rational -> Rational <-
      (ReadP.char '-' $> negate) <|>
      (ReadP.char '+' $> id) <|>
@@ -1620,9 +1710,10 @@
    yfpart :: Maybe String <-
      (ReadP.char dsep *> fmap Just (ReadP.munch1 Char.isDigit) <* ReadP.eof) <|>
      (ReadP.eof $> Nothing)
-   pure $! sig $ case yfpart of
-     Nothing -> fromInteger (read ipart)
-     Just fpart -> read (ipart <> fpart) % (10 ^ length fpart)
+   let r = sig $ case yfpart of
+         Nothing -> fromInteger (read ipart)
+         Just fpart -> read (ipart <> fpart) % (10 ^ length fpart)
+   pure $! r / scaleToRational (decimalConf_scale ds)
 
 --------------------------------------------------------------------------------
 -- QuickCheck Arbitrary instances
@@ -1634,12 +1725,9 @@
   shrink = fmap fromInteger . QC.shrink . toInteger
 
 instance QC.Arbitrary SomeDiscrete where
-  arbitrary = do
-    let md = mkSomeDiscrete
-               <$> fmap T.pack QC.arbitrary
-               <*> QC.arbitrary
-               <*> QC.arbitrary
-    fromJust <$> QC.suchThat md isJust
+  arbitrary = mkSomeDiscrete <$> fmap T.pack QC.arbitrary
+                             <*> QC.arbitrary
+                             <*> QC.arbitrary
   shrink = \x -> withSomeDiscrete x (map toSomeDiscrete . QC.shrink)
 
 instance QC.Arbitrary (Dense currency) where
@@ -1672,3 +1760,24 @@
 
 instance QC.Arbitrary Approximation where
   arbitrary = QC.oneof [ pure Round, pure Floor, pure Ceiling, pure Truncate ]
+
+instance QC.Arbitrary Scale where
+  arbitrary = do
+    n <- QC.arbitrary
+    d <- QC.arbitrary
+    let r = (abs n + 1) % (abs d + 1)
+    pure (Scale r)
+
+instance QC.Arbitrary Separators where
+  arbitrary = do
+    let msep = QC.suchThat QC.arbitrary $ \c ->
+          not (Char.isDigit c) && not (Char.isControl c)
+    ds :: Char <- msep
+    yts :: Maybe Char <- QC.oneof
+      [ pure Nothing, fmap Just (QC.suchThat msep (/= ds)) ]
+    let Just out = mkSeparators ds yts
+    pure out
+
+instance QC.Arbitrary DecimalConf where
+  arbitrary = DecimalConf <$> QC.arbitrary <*> QC.arbitrary
+                          <*> QC.arbitrary <*> QC.arbitrary
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -17,6 +17,7 @@
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Lazy as BL
 import qualified Data.Char as Char
+import Data.Constraint (Dict(Dict))
 import Data.Maybe (catMaybes, fromJust, fromMaybe, isJust, isNothing)
 import Data.Proxy (Proxy(Proxy))
 import Data.Ratio ((%), numerator, denominator)
@@ -36,36 +37,27 @@
 
 import qualified Money
 import qualified Money.Internal as MoneyI
-  (rationalFromDecimal, rationalToDecimal)
 
 --------------------------------------------------------------------------------
 
--- | Generates a valid 'MoneyI.rationalToDecimal' result. Returns the thousand
--- and decimal separators as welland decimal separators as well.
-genDecimal :: QC.Gen (T.Text, Maybe Char, Char)
+-- | A wrapper around a decimal representation and its 'Money.DecimalConf',
+-- mostly just to benefit from its 'QC.Arbitrary' instance.
+data Decimal = Decimal Money.DecimalConf T.Text
+  deriving (Show)
+
+instance QC.Arbitrary Decimal where
+  arbitrary = uncurry (flip Decimal) <$> genDecimal
+  shrink (Decimal ds t) = Decimal <$> QC.shrink ds
+                                  <*> fmap T.pack (QC.shrink (T.unpack t))
+
+-- | Generates a valid 'MoneyI.rationalToDecimal' result using the returned
+-- 'Money.DecimalConf'.
+genDecimal :: QC.Gen (T.Text, Money.DecimalConf)
 genDecimal = do
   aprox :: Money.Approximation <- QC.arbitrary
-  plus :: Bool <- QC.arbitrary
-  digs :: Word8 <- QC.arbitrary
+  ds :: Money.DecimalConf <- QC.arbitrary
   r :: Rational <- (%) <$> QC.arbitrary <*> QC.suchThat QC.arbitrary (/= 0)
-  (yts, ds) <- genDecimalSeps
-  let
-      dec = fromMaybe
-          (error "genDecimal failed because rationalToDecimal returned Nothing")
-          (MoneyI.rationalToDecimal aprox plus yts ds digs r)
-  pure (dec, yts, ds)
-
--- | Generates valid separators for decimal representations (see genDecimal).
-genDecimalSeps :: QC.Gen (Maybe Char, Char)
-genDecimalSeps = do
-  let msep = QC.suchThat QC.arbitrary (not . Char.isDigit)
-  ds :: Char <- msep
-  yts :: Maybe Char <- genMaybe (QC.suchThat msep (/= ds))
-  pure (yts, ds)
-
-
-genMaybe :: QC.Gen a -> QC.Gen (Maybe a)
-genMaybe m = QC.oneof [pure Nothing, fmap Just m]
+  pure (MoneyI.rationalToDecimal ds aprox r, ds)
 
 --------------------------------------------------------------------------------
 
@@ -79,10 +71,12 @@
 tests =
   Tasty.testGroup "root"
   [ testCurrencies
+  , testHalfEvenRounding
   , testCurrencyUnits
   , testExchange
   , testRationalToDecimal
   , testRationalFromDecimal
+  , testMkSeparators
   , testDiscreteFromDecimal
   , testRawSerializations
   ]
@@ -99,10 +93,8 @@
 testCurrencyUnits :: Tasty.TestTree
 testCurrencyUnits =
   Tasty.testGroup "Currency units"
-  [ testDiscrete (Proxy :: Proxy "BTC") (Proxy :: Proxy "BTC")
-  , testDiscrete (Proxy :: Proxy "BTC") (Proxy :: Proxy "satoshi")
+  [ testDiscrete (Proxy :: Proxy "BTC") (Proxy :: Proxy "satoshi")
   , testDiscrete (Proxy :: Proxy "BTC") (Proxy :: Proxy "bitcoin")
-  , testDiscrete (Proxy :: Proxy "USD") (Proxy :: Proxy "USD")
   , testDiscrete (Proxy :: Proxy "USD") (Proxy :: Proxy "cent")
   , testDiscrete (Proxy :: Proxy "USD") (Proxy :: Proxy "dollar")
   , testDiscrete (Proxy :: Proxy "VUV") (Proxy :: Proxy "vatu")
@@ -202,6 +194,29 @@
          , "0"       --  6
          , "0"       --  7
          ]
+  , HU.testCase "Round: r5" $ do
+       render Money.Round r5 @?=
+         [ "0.50"   --  0
+         , "0.50"   --  1
+         , "+0.50"  --  2
+         , "+0.50"  --  3
+         , "0"      --  4
+         , "0"      --  5
+         , "0"      --  6
+         , "0"      --  7
+         ]
+  , HU.testCase "Round: negate r5" $ do
+       render Money.Round (negate r5) @?=
+         [ "-0.50"   --  0
+         , "-0.50"   --  1
+         , "-0.50"   --  2
+         , "-0.50"   --  3
+         , "0"       --  4
+         , "0"       --  5
+         , "0"       --  6
+         , "0"       --  7
+         ]
+
   , HU.testCase "Floor: r1" $ do
        render Money.Floor r1 @?=
          [ "1023004567.89"        --  0
@@ -290,6 +305,28 @@
          , "-1"      --  6
          , "-1"      --  7
          ]
+  , HU.testCase "Floor: r5" $ do
+       render Money.Floor r5 @?=
+         [ "0.50"   --  0
+         , "0.50"   --  1
+         , "+0.50"   --  2
+         , "+0.50"   --  3
+         , "0"      --  4
+         , "0"      --  5
+         , "0"      --  6
+         , "0"      --  7
+         ]
+  , HU.testCase "Floor: negate r5" $ do
+       render Money.Floor (negate r5) @?=
+         [ "-0.50"   --  0
+         , "-0.50"   --  1
+         , "-0.50"   --  2
+         , "-0.50"   --  3
+         , "-1"      --  4
+         , "-1"      --  5
+         , "-1"      --  6
+         , "-1"      --  7
+         ]
 
   , HU.testCase "Ceiling: r1" $ do
        render Money.Ceiling r1 @?=
@@ -379,6 +416,28 @@
          , "0"      --  6
          , "0"      --  7
          ]
+  , HU.testCase "Ceiling: r5" $ do
+       render Money.Ceiling r5 @?=
+         [ "0.50"   --  0
+         , "0.50"   --  1
+         , "+0.50"  --  2
+         , "+0.50"  --  3
+         , "1"      --  4
+         , "1"      --  5
+         , "+1"     --  6
+         , "+1"     --  7
+         ]
+  , HU.testCase "Ceiling: negate r5" $ do
+       render Money.Ceiling (negate r5) @?=
+         [ "-0.50"   --  0
+         , "-0.50"   --  1
+         , "-0.50"   --  2
+         , "-0.50"   --  3
+         , "0"      --  4
+         , "0"      --  5
+         , "0"      --  6
+         , "0"      --  7
+         ]
 
   , HU.testCase "Truncate: r1" $ do
       render Money.Truncate r1 @?= render Money.Floor r1
@@ -403,50 +462,114 @@
 
   , HU.testCase "Truncate: negate r4" $ do
       render Money.Truncate (negate r4) @?= render Money.Ceiling (negate r4)
+
+  , HU.testCase "Truncate: r5" $ do
+      render Money.Truncate r5 @?= render Money.Floor r5
+
+  , HU.testCase "Truncate: negate r5" $ do
+      render Money.Truncate (negate r5) @?= render Money.Ceiling (negate r5)
+
+  , HU.testCase "HalfEven: r1" $ do
+      render Money.HalfEven r1 @?= render Money.Round r1
+
+  , HU.testCase "HalfEven: negate r1" $ do
+      render Money.HalfEven (negate r1) @?= render Money.Round (negate r1)
+
+  , HU.testCase "HalfEven: r2" $ do
+      render Money.HalfEven r2 @?= render Money.Round r2
+
+  , HU.testCase "HalfEven: negate r2" $ do
+      render Money.HalfEven (negate r2) @?= render Money.Round (negate r2)
+
+  , HU.testCase "HalfEven: r3" $ do
+      render Money.HalfEven r3 @?= render Money.Round r3
+
+  , HU.testCase "HalfEven: negate r3" $ do
+      render Money.HalfEven (negate r3) @?= render Money.Round (negate r3)
+
+  , HU.testCase "HalfEven: r4" $ do
+      render Money.HalfEven r4 @?= render Money.Round r4
+
+  , HU.testCase "HalfEven: negate r4" $ do
+      render Money.HalfEven (negate r4) @?= render Money.Round (negate r4)
+
+  , HU.testCase "HalfEven: r5" $ do
+       render Money.HalfEven r5 @?=
+         [ "0.50"   --  0
+         , "0.50"   --  1
+         , "+0.50"  --  2
+         , "+0.50"  --  3
+         , "0"      --  4
+         , "0"      --  5
+         , "0"      --  6
+         , "0"      --  7
+         ]
+  , HU.testCase "HalfEven: negate r5" $ do
+       render Money.HalfEven (negate r5) @?=
+         [ "-0.50"   --  0
+         , "-0.50"   --  1
+         , "-0.50"   --  2
+         , "-0.50"   --  3
+         , "0"      --  4
+         , "0"      --  5
+         , "0"      --  6
+         , "0"      --  7
+         ]
+
   ]
   where
     r1 :: Rational = 1023004567895 % 1000
     r2 :: Rational = 123 % 100
     r3 :: Rational = 345 % 1000
     r4 :: Rational = 7 % 1000
+    r5 :: Rational = 1 % 2
 
     render :: Money.Approximation -> Rational -> [T.Text]
     render a r =
-      [ fromJust $ MoneyI.rationalToDecimal a False Nothing    '.' 2 r  --  0
-      , fromJust $ MoneyI.rationalToDecimal a False (Just ',') '.' 2 r  --  1
-      , fromJust $ MoneyI.rationalToDecimal a True  Nothing    '.' 2 r  --  2
-      , fromJust $ MoneyI.rationalToDecimal a True  (Just ',') '.' 2 r  --  3
-      , fromJust $ MoneyI.rationalToDecimal a False Nothing    '.' 0 r  --  4
-      , fromJust $ MoneyI.rationalToDecimal a False (Just ',') '.' 0 r  --  5
-      , fromJust $ MoneyI.rationalToDecimal a True  Nothing    '.' 0 r  --  6
-      , fromJust $ MoneyI.rationalToDecimal a True  (Just ',') '.' 0 r  --  7
+      [ MoneyI.rationalToDecimal ds_dd_2 a r        -- 0
+      , MoneyI.rationalToDecimal ds_tc_dd_2 a r     -- 1
+      , MoneyI.rationalToDecimal ds_p_dd_2 a r      -- 2
+      , MoneyI.rationalToDecimal ds_p_tc_dd_2 a r   -- 3
+      , MoneyI.rationalToDecimal ds_dd_0 a r        -- 4
+      , MoneyI.rationalToDecimal ds_tc_dd_0 a r     -- 5
+      , MoneyI.rationalToDecimal ds_p_dd_0 a r      -- 6
+      , MoneyI.rationalToDecimal ds_p_tc_dd_0 a r   -- 7
       ]
 
-testRationalFromDecimal :: Tasty.TestTree
-testRationalFromDecimal =
-  Tasty.testGroup "rationalFromDecimal"
+testMkSeparators :: Tasty.TestTree
+testMkSeparators =
+  Tasty.testGroup "mkSeparators"
   [ QC.testProperty "Unsupported separators" $
-      let mbadsep :: QC.Gen Char = QC.suchThat QC.arbitrary Char.isDigit
-          mgoodsep :: QC.Gen Char = QC.suchThat QC.arbitrary (not . Char.isDigit)
+      let mbadsep :: QC.Gen Char = QC.suchThat QC.arbitrary $ \c ->
+            Char.isDigit c || Char.isControl c
+          mgoodsep :: QC.Gen Char = QC.suchThat QC.arbitrary $ \c ->
+            not (Char.isDigit c || Char.isControl c)
       in QC.forAll ((,,) <$> mbadsep <*> mbadsep <*> mgoodsep) $
            \(s1 :: Char, s2 :: Char, s3 :: Char) ->
-              let f = MoneyI.rationalFromDecimal
-              in (f Nothing s1 (error "untouched") === Nothing) .&&.
-                 (f (Just s1) s2 (error "untouched") === Nothing) .&&.
-                 (f (Just s1) s1 (error "untouched") === Nothing) .&&.
-                 (f (Just s3) s3 (error "untouched") === Nothing)
+               (Money.mkSeparators s1 Nothing   === Nothing) .&&.
+               (Money.mkSeparators s2 (Just s1) === Nothing) .&&.
+               (Money.mkSeparators s1 (Just s1) === Nothing) .&&.
+               (Money.mkSeparators s3 (Just s3) === Nothing)
+  ]
 
-  , Tasty.localOption (QC.QuickCheckTests 1000) $
+testRationalFromDecimal :: Tasty.TestTree
+testRationalFromDecimal =
+  Tasty.testGroup "rationalFromDecimal"
+  [ Tasty.localOption (QC.QuickCheckTests 1000) $
     QC.testProperty "Lossy roundtrip" $
       -- We check that the roundtrip results in a close amount with a fractional
       -- difference of up to one.
-      let gen = (,) <$> genDecimalSeps <*> QC.arbitrary
-      in QC.forAll gen $ \( (yst :: Maybe Char, sd :: Char)
-                          , (r :: Rational, plus :: Bool, digs :: Word8,
-                             aprox :: Money.Approximation) ) ->
-           let Just dec = MoneyI.rationalToDecimal aprox plus yst sd digs r
-               Just r' = MoneyI.rationalFromDecimal yst sd dec
-           in 1 > abs (abs r - abs r')
+      QC.forAll QC.arbitrary $ \(ds0 :: Money.DecimalConf,
+                                 aprox :: Money.Approximation,
+                                 r :: Rational) ->
+        let Just sr = Money.scaleFromRational
+                    $ (+ 1) -- scales less than 1 can't reliably roundtrip.
+                    $ Money.scaleToRational
+                    $ Money.decimalConf_scale ds0
+            ds1 = ds0 { Money.decimalConf_scale = sr }
+            dec = MoneyI.rationalToDecimal ds1 aprox r
+            Just r' = MoneyI.rationalFromDecimal ds1 dec
+        in 1 > abs (abs r - abs r')
   ]
 
 testDense
@@ -489,42 +612,39 @@
         T.unpack (Money.denseCurrency x) === symbolVal pc
 
   , QC.testProperty "denseToDecimal: Same as rationalToDecimal" $
-      let gen = (,) <$> genDecimalSeps <*> QC.arbitrary
-      in QC.forAll gen $ \( (yst :: Maybe Char, sd :: Char)
-                          , (dns :: Money.Dense currency, plus :: Bool,
-                             digs :: Word8, aprox :: Money.Approximation) ) ->
-            let ydnsd1 = Money.denseToDecimal aprox plus yst sd digs 1 dns
-                ydnsd100 = Money.denseToDecimal aprox plus yst sd digs 100 dns
-                yrd1 = MoneyI.rationalToDecimal aprox plus yst sd digs (toRational dns)
-                yrd100 = MoneyI.rationalToDecimal aprox plus yst sd digs (toRational dns * 100)
-            in (ydnsd1 === yrd1) .&&. (ydnsd100 === yrd100)
-
-  , QC.testProperty "denseFromDecimal: Same as rationalFromDecimal" $
-      QC.forAll genDecimal $ \(dec :: T.Text, yts :: Maybe Char, ds :: Char) ->
-         let Just r = MoneyI.rationalFromDecimal yts ds dec
-             Just dns = Money.denseFromDecimal yts ds 1 dec
-         in r === toRational (dns :: Money.Dense currency)
+      QC.forAll QC.arbitrary $ \(ds :: Money.DecimalConf,
+                                 dns :: Money.Dense currency,
+                                 aprox :: Money.Approximation) ->
+         let Just s100 = Money.scaleFromRational
+                    (Money.scaleToRational (Money.decimalConf_scale ds) * 100)
+             ds_s100 = ds { Money.decimalConf_scale = s100 }
+             ydnsd1 = Money.denseToDecimal ds aprox dns
+             ydnsd100 = Money.denseToDecimal ds_s100 aprox dns
+             yrd1 = MoneyI.rationalToDecimal ds aprox (toRational dns)
+             yrd100 = MoneyI.rationalToDecimal ds aprox (toRational dns * 100)
+         in (ydnsd1 === yrd1) .&&. (ydnsd100 === yrd100)
 
   , QC.testProperty "denseFromDecimal: Same as rationalFromDecimal" $
-      QC.forAll genDecimal $ \(dec :: T.Text, yts :: Maybe Char, ds :: Char) ->
-         let Just r = MoneyI.rationalFromDecimal yts ds dec
-             Just dns = Money.denseFromDecimal yts ds 1 dec
+      QC.forAll QC.arbitrary $ \(Decimal ds dec) ->
+         let Just r = MoneyI.rationalFromDecimal ds dec
+             Just dns = Money.denseFromDecimal ds dec
          in r === toRational (dns :: Money.Dense currency)
 
-
   , Tasty.localOption (QC.QuickCheckTests 1000) $
     QC.testProperty "denseToDecimal/denseFromDiscrete: Lossy roundtrip" $
       -- We check that the roundtrip results in a close amount with a fractional
       -- difference of up to one.
-      let gen = (,) <$> genDecimalSeps <*> QC.arbitrary
-      in QC.forAll gen $ \( (yst :: Maybe Char, sd :: Char)
-                          , (sc0 :: Rational, plus :: Bool,
-                             digs :: Word8, aprox :: Money.Approximation,
-                             dns :: Money.Dense currency) ) ->
-           let sc = abs sc0 + 1 -- smaller scales can't reliably be parsed back
-               Just dec = Money.denseToDecimal aprox plus yst sd digs sc dns
-               Just dns' = Money.denseFromDecimal yst sd sc dec
-           in Money.dense' 1 > abs (abs dns - abs dns')
+      QC.forAll QC.arbitrary $ \(ds0 :: Money.DecimalConf,
+                                 aprox :: Money.Approximation,
+                                 dns :: Money.Dense currency) ->
+        let Just sr = Money.scaleFromRational
+                    $ (+ 1) -- scales less than 1 can't reliably roundtrip.
+                    $ Money.scaleToRational
+                    $ Money.decimalConf_scale ds0
+            ds1 = ds0 { Money.decimalConf_scale = sr }
+            dec = Money.denseToDecimal ds1 aprox dns
+            Just dns' = Money.denseFromDecimal ds1 dec
+        in Money.dense' 1 > abs (abs dns - abs dns')
 
   , HU.testCase "AdditiveGroup: zeroV" $
       (AG.zeroV :: Money.Dense currency) @?= Money.dense' (0%1)
@@ -587,7 +707,7 @@
 
 testDiscrete
   :: forall (currency :: Symbol) (unit :: Symbol)
-  .  ( Money.GoodScale (Money.Scale currency unit)
+  .  ( Money.GoodScale (Money.UnitScale currency unit)
      , KnownSymbol currency
      , KnownSymbol unit )
   => Proxy currency
@@ -615,7 +735,7 @@
       QC.forAll QC.arbitrary $ \(dr :: Money.SomeDiscrete) ->
         ((T.unpack (Money.someDiscreteCurrency dr) /= symbolVal pc) &&
          (Money.someDiscreteScale dr /=
-             Money.scale (Proxy :: Proxy (Money.Scale currency unit)))
+             Money.scale (Proxy :: Proxy (Money.UnitScale currency unit)))
         ) ==> isNothing (Money.fromSomeDiscrete dr
                           :: Maybe (Money.Discrete currency unit))
   , QC.testProperty "withSomeDiscrete" $
@@ -633,16 +753,14 @@
   , QC.testProperty "discreteToDecimal/discreteFromDecimal: Same as denseToDecimal/denseFromDecimal" $
       -- We check that the roundtrip results in a close amount with a fractional
       -- difference of up to one.
-      let gen = (,) <$> genDecimalSeps <*> QC.arbitrary
-      in QC.forAll gen $ \( (yst :: Maybe Char, sd :: Char)
-                          , (sc0 :: Rational, plus :: Bool,
-                             digs :: Word8, aprox :: Money.Approximation,
-                             dis :: Money.Discrete currency unit) ) ->
-           let sc = abs sc0 + 1  -- scale can't be less than 1
-               dns = Money.denseFromDiscrete dis
-               ydec = Money.discreteToDecimal aprox plus yst sd digs sc dis
-               ydec' = Money.denseToDecimal aprox plus yst sd digs sc dns
-           in ydec === ydec'
+      QC.forAll QC.arbitrary $ \(ds :: Money.DecimalConf,
+                                 sc0 :: Rational,
+                                 aprox :: Money.Approximation,
+                                 dis :: Money.Discrete currency unit) ->
+        let sc = abs sc0 + 1  -- scales less than 1 can't reliably roundtrip.
+            dns = Money.denseFromDiscrete dis
+        in Money.discreteToDecimal ds aprox dis
+             === Money.denseToDecimal ds aprox dns
 
   , HU.testCase "AdditiveGroup: zeroV" $
       (AG.zeroV :: Money.Discrete currency unit) @?= Money.discrete 0
@@ -751,19 +869,17 @@
              (show x, dr) === (show x', Money.toSomeExchangeRate x')
 
   , QC.testProperty "exchangeRateToDecimal: Same as rationalToDecimal" $
-      let gen = (,) <$> genDecimalSeps <*> QC.arbitrary
-      in QC.forAll gen $ \( (yst :: Maybe Char, sd :: Char)
-                          , (xr :: Money.ExchangeRate src dst, digs :: Word8,
-                             aprox :: Money.Approximation ) ) ->
-           let xrd = Money.exchangeRateToDecimal aprox yst sd digs xr
-               rd = MoneyI.rationalToDecimal aprox False yst sd digs
-                       (Money.exchangeRateToRational xr)
-           in xrd === rd
+      QC.forAll QC.arbitrary $ \(ds :: Money.DecimalConf,
+                                 xr :: Money.ExchangeRate src dst,
+                                 aprox :: Money.Approximation) ->
+        Money.exchangeRateToDecimal ds aprox xr
+          === MoneyI.rationalToDecimal ds aprox
+                (Money.exchangeRateToRational xr)
 
   , QC.testProperty "exchangeRateFromDecimal: Same as rationalFromDecimal" $
-      QC.forAll genDecimal $ \(dec :: T.Text, yts :: Maybe Char, ds :: Char) ->
-         let Just r = MoneyI.rationalFromDecimal yts ds dec
-             yxr = Money.exchangeRateFromDecimal yts ds dec
+      QC.forAll QC.arbitrary $ \(Decimal ds dec) ->
+         let Just r = MoneyI.rationalFromDecimal ds dec
+             yxr = Money.exchangeRateFromDecimal ds dec
                       :: Maybe (Money.ExchangeRate src dst)
          in (r > 0) ==> (Just r === fmap Money.exchangeRateToRational yxr)
 
@@ -793,57 +909,60 @@
 testDiscreteFromDecimal =
   Tasty.testGroup "discreteFromDecimal"
   [ HU.testCase "Too large" $ do
-      Money.discreteFromDecimal Nothing '.' 1 "0.053"
+      Money.discreteFromDecimal ds_dd_2 "0.053"
         @?= (Nothing :: Maybe (Money.Discrete "USD" "cent"))
-      Money.discreteFromDecimal (Just ',') '.' 1 "0.253"
+      Money.discreteFromDecimal ds_tc_dd_2 "0.253"
         @?= (Nothing :: Maybe (Money.Discrete "USD" "cent"))
 
   , HU.testCase "USD cent, small, zero" $ do
       let dis = 0 :: Money.Discrete "USD" "cent"
-          f = Money.discreteFromDecimal
-      f Nothing '.' 1 "0" @?= Just dis
-      f Nothing '.' 1 "+0" @?= Just dis
-      f Nothing '.' 1 "-0" @?= Just dis
-      f (Just ',') '.' 1 "0" @?= Just dis
-      f (Just ',') '.' 1 "+0" @?= Just dis
-      f (Just ',') '.' 1 "-0" @?= Just dis
+      Money.discreteFromDecimal ds_dd_2       "0" @?= Just dis
+      Money.discreteFromDecimal ds_dd_2      "+0" @?= Just dis
+      Money.discreteFromDecimal ds_dd_2      "-0" @?= Just dis
+      Money.discreteFromDecimal ds_p_dd_2     "0" @?= Just dis
+      Money.discreteFromDecimal ds_p_dd_2    "+0" @?= Just dis
+      Money.discreteFromDecimal ds_p_dd_2    "-0" @?= Just dis
 
   , HU.testCase "USD cent, small, positive" $ do
       let dis = 25 :: Money.Discrete "USD" "cent"
-          f = Money.discreteFromDecimal
-      f Nothing '.' 1 "0.25" @?= Just dis
-      f Nothing '.' 1 "+0.25" @?= Just dis
-      f (Just ',') '.' 1 "0.25" @?= Just dis
-      f (Just ',') '.' 1 "+0.25" @?= Just dis
+      Money.discreteFromDecimal ds_dd_2      "0.25"  @?= Just dis
+      Money.discreteFromDecimal ds_dd_2      "+0.25" @?= Just dis
+      Money.discreteFromDecimal ds_tc_dd_2   "0.25"  @?= Just dis
+      Money.discreteFromDecimal ds_tc_dd_2   "+0.25" @?= Just dis
+      Money.discreteFromDecimal ds_p_dd_2    "0.25"  @?= Just dis
+      Money.discreteFromDecimal ds_p_dd_2    "+0.25" @?= Just dis
+      Money.discreteFromDecimal ds_p_tc_dd_2 "0.25"  @?= Just dis
+      Money.discreteFromDecimal ds_p_tc_dd_2 "+0.25" @?= Just dis
 
   , HU.testCase "USD cent, small, negative" $ do
       let dis = -25 :: Money.Discrete "USD" "cent"
-          f = Money.discreteFromDecimal
-      f Nothing '.' 1 "-0.25" @?= Just dis
-      f Nothing '.' 1 "-0.25" @?= Just dis
-      f (Just ',') '.' 1 "-0.25" @?= Just dis
-      f (Just ',') '.' 1 "-0.25" @?= Just dis
+      Money.discreteFromDecimal ds_dd_2      "-0.25" @?= Just dis
+      Money.discreteFromDecimal ds_p_dd_2    "-0.25" @?= Just dis
+      Money.discreteFromDecimal ds_tc_dd_2   "-0.25" @?= Just dis
+      Money.discreteFromDecimal ds_p_tc_dd_2 "-0.25" @?= Just dis
 
   , HU.testCase "USD cent, big, positive" $ do
       let dis = 102300456789 :: Money.Discrete "USD" "cent"
-          f = Money.discreteFromDecimal
-      f Nothing '.' 1 "1023004567.89" @?= Just dis
-      f Nothing '.' 1 "+1023004567.89" @?= Just dis
-      f (Just ',') '.' 1 "1,023,004,567.89" @?= Just dis
-      f (Just ',') '.' 1 "+1,023,004,567.89" @?= Just dis
+      Money.discreteFromDecimal ds_dd_2      "+1023004567.89"    @?= Just dis
+      Money.discreteFromDecimal ds_dd_2      "1023004567.89"     @?= Just dis
+      Money.discreteFromDecimal ds_p_dd_2    "+1023004567.89"    @?= Just dis
+      Money.discreteFromDecimal ds_p_dd_2    "1023004567.89"     @?= Just dis
+      Money.discreteFromDecimal ds_p_tc_dd_2 "+1,023,004,567.89" @?= Just dis
+      Money.discreteFromDecimal ds_p_tc_dd_2 "1,023,004,567.89"  @?= Just dis
+      Money.discreteFromDecimal ds_tc_dd_2   "+1,023,004,567.89" @?= Just dis
+      Money.discreteFromDecimal ds_tc_dd_2   "1,023,004,567.89"  @?= Just dis
 
   , HU.testCase "USD cent, big, negative" $ do
       let dis = -102300456789 :: Money.Discrete "USD" "cent"
-          f = Money.discreteFromDecimal
-      f Nothing '.' 1 "-1023004567.89" @?= Just dis
-      f Nothing '.' 1 "-1023004567.89" @?= Just dis
-      f (Just ',') '.' 1 "-1,023,004,567.89" @?= Just dis
-      f (Just ',') '.' 1 "-1,023,004,567.89" @?= Just dis
+      Money.discreteFromDecimal ds_dd_2      "-1023004567.89" @?= Just dis
+      Money.discreteFromDecimal ds_p_dd_2    "-1023004567.89" @?= Just dis
+      Money.discreteFromDecimal ds_tc_dd_2   "-1,023,004,567.89" @?= Just dis
+      Money.discreteFromDecimal ds_p_tc_dd_2 "-1,023,004,567.89" @?= Just dis
   ]
 
 testRounding
   :: forall (currency :: Symbol) (unit :: Symbol)
-  .  (Money.GoodScale (Money.Scale currency unit), KnownSymbol currency)
+  .  (Money.GoodScale (Money.UnitScale currency unit), KnownSymbol currency)
   => Proxy currency
   -> Proxy unit
   -> Tasty.TestTree
@@ -853,24 +972,98 @@
     , QC.testProperty "ceiling"  $ QC.forAll QC.arbitrary (g (Money.discreteFromDense Money.Ceiling))
     , QC.testProperty "round"    $ QC.forAll QC.arbitrary (g (Money.discreteFromDense Money.Round))
     , QC.testProperty "truncate" $ QC.forAll QC.arbitrary (g (Money.discreteFromDense Money.Truncate))
+    , QC.testProperty "half-even" $ QC.forAll QC.arbitrary (g (Money.discreteFromDense Money.HalfEven))
     , QC.testProperty "floor no reminder"    $ QC.forAll QC.arbitrary (h (Money.discreteFromDense Money.Floor))
     , QC.testProperty "ceiling no reminder"  $ QC.forAll QC.arbitrary (h (Money.discreteFromDense Money.Ceiling))
     , QC.testProperty "round no reminder"    $ QC.forAll QC.arbitrary (h (Money.discreteFromDense Money.Round))
     , QC.testProperty "truncate no reminder" $ QC.forAll QC.arbitrary (h (Money.discreteFromDense Money.Truncate))
+    , QC.testProperty "half-even no reminder " $ QC.forAll QC.arbitrary (h (Money.discreteFromDense Money.HalfEven))
     ]
   where
-    g :: (Money.Dense currency -> (Money.Discrete' currency (Money.Scale currency unit), Money.Dense currency))
+    g :: (Money.Dense currency -> (Money.Discrete' currency (Money.UnitScale currency unit), Money.Dense currency))
       -> Money.Dense currency
       -> QC.Property
     g f = \x -> x === case f x of (y, z) -> Money.denseFromDiscrete y + z
 
-    h :: (Money.Dense currency -> (Money.Discrete' currency (Money.Scale currency unit), Money.Dense currency))
+    h :: (Money.Dense currency -> (Money.Discrete' currency (Money.UnitScale currency unit), Money.Dense currency))
       -> Money.Discrete currency unit
       -> QC.Property
     h f = \x -> (Money.denseFromDiscrete x) === case f (Money.denseFromDiscrete x) of
       (y, 0) -> Money.denseFromDiscrete y
       (_, _) -> error "testRounding.h: unexpected"
 
+testHalfEvenRounding :: Tasty.TestTree
+testHalfEvenRounding =
+    Tasty.testGroup "HalfEven rounding"
+    [ HU.testCase "-2.8" $ -3 @=? f (-3152519739159347 % 1125899906842624)
+
+    , HU.testCase "-2.5" $ -2 @=? f (-5 % 2)
+    , HU.testCase "-2.2" $ -2 @=? f (-2476979795053773 % 1125899906842624)
+    , HU.testCase "-2"   $ -2 @=? f (-2 % 1)
+    , HU.testCase "-1.8" $ -2 @=? f (-8106479329266893 % 4503599627370496)
+    , HU.testCase "-1.5" $ -2 @=? f (-3 % 2)
+
+    , HU.testCase "-1.2" $ -1 @=? f (-5404319552844595 % 4503599627370496)
+    , HU.testCase "-1"   $ -1 @=? f (-1 % 1)
+    , HU.testCase "-0.8" $ -1 @=? f (-3602879701896397 % 4503599627370496)
+
+    , HU.testCase "-0.5" $  0 @=? f (-1 % 2)
+    , HU.testCase "-0.2" $  0 @=? f (-3602879701896397 % 18014398509481984)
+    , HU.testCase  "0"   $  0 @=? f (0 % 1)
+    , HU.testCase  "0.2" $  0 @=? f (3602879701896397 % 18014398509481984)
+    , HU.testCase  "0.5" $  0 @=? f (1 % 2)
+
+    , HU.testCase  "0.8" $  1 @=? f (3602879701896397 % 4503599627370496)
+    , HU.testCase  "1"   $  1 @=? f (1 % 1)
+    , HU.testCase  "1.2" $  1 @=? f (5404319552844595 % 4503599627370496)
+
+    , HU.testCase  "1.5" $  2 @=? f (3 % 2)
+    , HU.testCase  "1.8" $  2 @=? f (8106479329266893 % 4503599627370496)
+    , HU.testCase  "2"   $  2 @=? f (2 % 1)
+    , HU.testCase  "2.2" $  2 @=? f (2476979795053773 % 1125899906842624)
+    , HU.testCase  "2.5" $  2 @=? f (5 % 2)
+
+    , HU.testCase  "2.8" $  3 @=? f (3152519739159347 % 1125899906842624)
+    ]
+  where
+    f :: Rational -> Integer
+    f = MoneyI.approximate MoneyI.HalfEven
+
+
+
+-- | Decimal dot, 2 decimals
+ds_dd_2 :: Money.DecimalConf
+ds_dd_2 = Money.defaultDecimalConf
+
+-- | Leading plus, decimal dot, 2 decimals
+ds_p_dd_2 :: Money.DecimalConf
+ds_p_dd_2 = ds_dd_2 { Money.decimalConf_leadingPlus = True }
+
+-- | Thousands comma, decimal dot, 2 decimals
+ds_tc_dd_2 :: Money.DecimalConf
+ds_tc_dd_2 = ds_dd_2 { Money.decimalConf_separators = Money.separatorsDotComma }
+
+-- | Leading plus, thousands comma, decimal dot, 2 decimals
+ds_p_tc_dd_2 :: Money.DecimalConf
+ds_p_tc_dd_2 = ds_tc_dd_2 { Money.decimalConf_leadingPlus = True }
+
+-- | Decimal dot, 0 decimals
+ds_dd_0 :: Money.DecimalConf
+ds_dd_0 = ds_dd_2 { Money.decimalConf_digits = 0 }
+
+-- | Leading plus, decimal dot, 0 decimals
+ds_p_dd_0 :: Money.DecimalConf
+ds_p_dd_0 = ds_p_dd_2 { Money.decimalConf_digits = 0 }
+
+-- | Thousands comma, decimal dot, 0 decimals
+ds_tc_dd_0 :: Money.DecimalConf
+ds_tc_dd_0 = ds_tc_dd_2 { Money.decimalConf_digits = 0 }
+
+-- | Leading plus, thousands comma, decimal dot, 0 decimals
+ds_p_tc_dd_0 :: Money.DecimalConf
+ds_p_tc_dd_0 = ds_p_tc_dd_2 { Money.decimalConf_digits = 0 }
+
+
 --------------------------------------------------------------------------------
 -- Raw parsing "golden tests"
 
@@ -913,6 +1106,16 @@
 rawDis0_binary = "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\ETXUSD\NUL\NUL\NUL\NULd\NUL\NUL\NUL\NUL\SOH\NUL\NUL\NUL\NUL\EOT"
 rawXr0_binary :: BL.ByteString
 rawXr0_binary = "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\ETXUSD\NUL\NUL\NUL\NUL\NUL\NUL\NUL\ETXBTC\NUL\NUL\NUL\NUL\ETX\NUL\NUL\NUL\NUL\STX"
+
+--------------------------------------------------------------------------------
+
+-- Just checking that this compiles.
+_testCurrencyScale
+  :: Dict (Money.CurrencyScale "USD" ~ Money.UnitScale "USD" "cent",
+           Money.CurrencyScale "BTC" ~ Money.UnitScale "BTC" "satoshi",
+           Money.CurrencyScale "VUV" ~ Money.UnitScale "VUV" "vatu",
+           Money.CurrencyScale "XAU" ~ Money.ErrScaleNonCanonical "XAU")
+_testCurrencyScale = Dict
 
 --------------------------------------------------------------------------------
 -- Misc
