Skip to main content

Command Palette

Search for a command to run...

How to Safely Handle Currencies in Code with ISO 4217

Updated
•4 min read•View as Markdown
How to Safely Handle Currencies in Code with ISO 4217

Storing money as integers prevents floating-point precision loss, but hardcoding a 100x multiplier assumes every currency works like USD or EUR. Under ISO 4217, currencies vary from zero decimal places (JPY) to three (KWD). To avoid catastrophic overcharging or rounding loss, treat currency decimal scale as dynamic metadata tied to the ISO code.

Every software engineer learns the golden rule of financial software early on: never store monetary values as floating-point numbers. Floating-point arithmetic introduces IEEE 754 precision errors that turn fractions of a cent into accounting discrepancies.

So, we switch to integers, store values in the currency's smallest minor unit, and move on. But there is a hidden trap hiding inside that naive solution.

Why Does the "Multiply by 100" Strategy Fail Globally?

Hardcoding a 100 multiplier assumes every global currency uses two decimal places, which is incorrect. ISO 4217 defines over 30 zero-decimal currencies and several three-decimal currencies. Applying a fixed multiplier leads to severe overcharging or truncation errors depending on the target currency.

Imagine you run an e-commerce platform expanding into Japan. A customer buys a coffee for 500 Japanese Yen (JPY). If your payment service naively executes amount * 100 before passing the integer to an API like Stripe, it sends 50000.

Because payment gateways respect ISO 4217 standards, they interpret zero-decimal currencies as whole units. Your script just charged the customer 50,000 yen for a 500-yen coffee.

The bug bites in reverse for three-decimal currencies like the Kuwaiti Dinar (KWD), where 1 Dinar divides into 1,000 fils. Assuming two decimal places makes it impossible to represent the third digit, silently chopping off fils as a rounding error on every transaction.

How Does ISO 4217 Define Currency Minor Units?

The ISO 4217 standard assigns an explicit minor unit exponent (scale) to every official currency code. This exponent represents the number of decimal places required to convert a major currency unit into its smallest sub-unit.

Money isn't uniform. You cannot treat currency as a pure numeric value; it is a composite structure of a value and its ISO 4217 metadata.

Here is how common ISO 4217 currencies break down by decimal exponent:

Currency Code Currency Name Decimal Exponent Minor Unit Name Example (1 Major Unit in Minor Units)
USD US Dollar 2 Cent 100 cents
JPY Japanese Yen 0 N/A 1 Yen
KWD Kuwaiti Dinar 3 Fils 1,000 fils
BHD Bahraini Dinar 3 Fils 1,000 fils
CLF Unidad de Fomento 4 Unidades 10,000 unidades

Notice that while two decimal places is common, it is far from universal. Over 30 currencies have zero decimal places, seven have three, and some non-standard currencies do not even use base 10.

How Do You Safely Handle Currency Conversion in Code?

Rather than multiplying by a hardcoded number, look up the currency exponent dynamically from a verified ISO 4217 registry. Use exponent-based scaling (10^scale) or specialized currency handling libraries to convert major units into minor units.

Here is how you can look up the exponent and compute the minor unit dynamically:

const ISO_CURRENCY_EXPONENTS: Record<string, number> = {
  USD: 2, EUR: 2, GBP: 2, JPY: 0, KWD: 3, BHD: 3
};

function toMinorUnits(amount: number, currencyCode: string): number {
  const exponent = ISO_CURRENCY_EXPONENTS[currencyCode.toUpperCase()];
  if (exponent === undefined) {
    throw new Error(`Unsupported currency code: ${currencyCode}`);
  }
  return Math.round(amount * Math.pow(10, exponent));
}

By looking up the exponent dynamically, toMinorUnits(500, 'JPY') correctly returns 500, while toMinorUnits(1.5, 'KWD') returns 1500.

Frequently Asked Questions

Which ISO 4217 currencies have zero decimal places?

Over 30 currencies do not use minor units, including the Japanese Yen (JPY), South Korean Won (KRW), Chilean Peso (CLP), and Vietnamese Dong (VND). When interacting with payment processors, these values must be passed as unscaled integers.

How do payment APIs like Stripe handle zero-decimal currencies?

Stripe expects monetary values in the smallest currency unit but respects ISO 4217 exponent definitions. For USD, sending 1000 means $10.00. For JPY, sending 1000 means 1,000 Yen.

Should I store money as integers or decimals in a database?

Store monetary amounts as integers representing the minor unit alongside the three-letter ISO 4217 currency code. Alternatively, store major units in arbitrary-precision decimal columns (e.g., DECIMAL(18, 4)) to accommodate high-scale precision safely.