[{"data":1,"prerenderedAt":38},["ShallowReactive",2],{"blog-post-store-money-as-integer-cents-never-a-float":3},{"slug":4,"title":5,"description":6,"date":7,"updated":8,"tags":9,"pillar":14,"draft":15,"image":16,"imageAlt":17,"html":18,"markdown":19,"faq":20,"readingTimeText":36,"readingTimeMinutes":37},"store-money-as-integer-cents-never-a-float","Store money as integer cents, never a float","An opinionated rule for money code: store amounts as integer minor units, never a binary float, and never divide a total to get its parts.","2026-07-11T00:00:00.000Z",null,[10,11,12,13],"payments","money","databases","best-practices","revenue-critical-code",false,"/blog-covers/revenue-critical-code.png","Revenue-critical code cover","\u003Cdiv class=\"post-tldr\">\u003Cdiv class=\"post-tldr-label\">TL;DR\u003C/div>\u003Cp>Represent money as an integer count of the smallest currency unit, 1234 for $12.34, never a binary float. Floats cannot hold most decimal amounts exactly, so they leak fractions of a cent that turn into a settlement that does not reconcile.\u003C/p>\u003C/div>\n\u003Cp>Store money as an integer count of the smallest currency unit. 1234 means $12.34. Do not use a binary float, ever, for an amount you will add, compare, or refund. This is not a style preference. It is the difference between books that reconcile and a slow leak you only find when someone audits settlement.\u003C/p>\n\u003Ch2 id=\"why-a-float-cannot-hold-%242.78\" tabindex=\"-1\">Why a float cannot hold $2.78\u003C/h2>\n\u003Cp>IEEE 754 binary floats store numbers as sums of powers of two. Most decimal fractions, including 0.1 and 2.78, have no exact binary form, so the machine keeps the nearest approximation. The famous demonstration is one line in almost every language:\u003C/p>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">0.1\u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C/span>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> 0.2\u003C/span>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">            // 0.30000000000000004\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">0.1\u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> +\u003C/span>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> 0.2\u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> ===\u003C/span>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> 0.3\u003C/span>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">    // false\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>PHP, Python, and Java doubles do the same thing. The value looks right when you print it with two decimals, which is exactly why this bug survives to production. The moment you add, multiply, or compare these amounts, the tiny error compounds, and as \u003Ca href=\"https://www.moderntreasury.com/journal/floats-dont-work-for-storing-cents\">Modern Treasury explains in their writeup on why floats do not work for cents\u003C/a>, every workaround (round to two places, compare within an epsilon) is a patch on a representation that was wrong for money from the start.\u003C/p>\n\u003Ch2 id=\"integer-minor-units%2C-the-boring-correct-default\" tabindex=\"-1\">Integer minor units, the boring correct default\u003C/h2>\n\u003Cp>Store the amount as a whole number of the smallest unit the currency has. $12.34 is 1234. This is what \u003Ca href=\"https://docs.stripe.com/currencies\">Stripe\u003C/a> does: every amount in its API is a positive integer in the smallest currency unit, so 1000 charges ten dollars, and 10 charges ten yen, because JPY is a zero-decimal currency with no minor unit. Integers add, subtract, and compare exactly. A database \u003Ccode>BIGINT\u003C/code> is cheap, unambiguous, and immune to the epsilon dance.\u003C/p>\n\u003Cp>The single discipline this asks of you: format to a decimal string only at the very edge, when you render to a human or hand the amount to a gateway. Everywhere in between, it is an integer.\u003C/p>\n\u003Ch2 id=\"the-rule-that-actually-saves-you%3A-never-divide-a-total\" tabindex=\"-1\">The rule that actually saves you: never divide a total\u003C/h2>\n\u003Cp>The float question gets the attention, but the bug I see more often is dividing a total to get its parts. Split $10.00 three ways by dividing and you get 333 plus 333 plus 333, which is 999, and a cent has vanished into the floor function. I walked through this exact failure in a payments context in \u003Ca href=\"/blog/six-payments-edge-cases-that-bite-at-2am\">six payments edge cases that bite at 2am\u003C/a>.\u003C/p>\n\u003Cp>The fix is to allocate, not divide: give each part the floor, then hand out the remainder one unit at a time until it is gone. Martin Fowler named this decades ago as the \u003Ca href=\"https://martinfowler.com/eaaCatalog/money.html\">Money pattern’s \u003Ccode>allocate()\u003C/code>\u003C/a>, which exists precisely so you avoid losing pennies to rounding.\u003C/p>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">function\u003C/span>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> allocate\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">int\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> $totalCents, \u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">int\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> $parts)\u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">:\u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> array\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    $base \u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C/span>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> intdiv\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">($totalCents, $parts);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    $remainder \u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> $totalCents \u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">-\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> $base \u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">*\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> $parts;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    $result \u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C/span>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> array_fill\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">0\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, $parts, $base);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    for\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> ($i \u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">=\u003C/span>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> 0\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">; $i \u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">&#x3C;\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> $remainder; $i\u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">++\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">        $result[$i]\u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">++\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">;   \u003C/span>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">// distribute the leftover, one cent at a time\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">    }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">    return\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> $result;      \u003C/span>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">// 1000 over 3 -> [334, 333, 333], sums back to 1000\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>This is the same discipline that keeps a refund honest and keeps a mid-cycle plan change honest. It is why I read the refund path first when I audit a codebase, in \u003Ca href=\"/blog/the-refund-path-is-where-i-read-a-codebases-soul\">the refund path is where I read a codebase’s soul\u003C/a>, and why proration is where subscription systems quietly leak, in \u003Ca href=\"/blog/subscriptions-are-licensing-that-bills-on-time\">subscriptions are just licensing that bills on time\u003C/a>.\u003C/p>\n\u003Ch2 id=\"the-honest-counterargument\" tabindex=\"-1\">The honest counterargument\u003C/h2>\n\u003Cp>There is a reasonable objection, argued well in \u003Ca href=\"https://world.hey.com/otar/storing-money-as-integer-cents-is-often-over-engineering-7238a485\">Storing money as integer cents is often over-engineering\u003C/a>: integer cents can be more ceremony than a small app needs, and an exact \u003Ccode>DECIMAL\u003C/code> column with a money library does the job. I agree with half of it. A SQL \u003Ccode>DECIMAL\u003C/code> (or \u003Ccode>NUMERIC\u003C/code>) is exact, fixed-point, base-10 arithmetic, not a float, and \u003Ca href=\"https://cardinalby.github.io/blog/post/best-practices/storing-currency-values-data-types/\">every serious database has had it for decades\u003C/a>. If your stack has a first-class decimal type and you use it consistently, you will not leak money.\u003C/p>\n\u003Cp>What I will not accept is a binary float, and I will not accept deriving a part by dividing a whole. So pick one exact representation, integer minor units or a real decimal type, commit to it, and never mix the two in the same column.\u003C/p>\n\u003Ch2 id=\"what-this-looks-like-in-a-schema\" tabindex=\"-1\">What this looks like in a schema\u003C/h2>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">-- good: exact, unambiguous, integer minor units\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">amount_cents  \u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">BIGINT\u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">        NOT NULL\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,   \u003C/span>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">-- 1234 = $12.34\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">currency      \u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">CHAR\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">3\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">)       \u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">NOT NULL\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,   \u003C/span>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">-- 'USD'\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">-- also fine, if you commit to it everywhere\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">amount        \u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">NUMERIC\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">14\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">2\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) \u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">NOT NULL\u003C/span>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">-- never\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">amount        \u003C/span>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">DOUBLE PRECISION\u003C/span>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">           -- a slow leak with a two-decimal disguise\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\u003C/code>\u003C/pre>\n\u003Cp>Store the currency next to the amount. An amount without its currency is Fowler’s other warning: sooner or later you add dollars to yen and nobody notices until the total is wrong.\u003C/p>\n\u003Cdiv class=\"post-takeaway\">\u003Cdiv class=\"post-takeaway-label\">The takeaway\u003C/div>\u003Cp>Money is a count, not a measurement, so represent it as an integer of the smallest unit, keep the currency beside it, and never derive a part by dividing a whole. Do that and your refunds, proration, and reconciliation stay honest. Skip it and you ship a leak that hides until the numbers stop matching at settlement, which is the most expensive place to find it.\u003C/p>\u003C/div>\u003Ch2 id=\"faq\" tabindex=\"-1\">FAQ\u003C/h2>\n\u003Cdiv class=\"post-faq\">\u003Cdiv class=\"faq-item\">\u003Cdiv class=\"faq-q\">Is a SQL DECIMAL just a float under the hood?\u003C/div>\u003Cdiv class=\"faq-a\">\u003Cp>No. \u003Ccode>DECIMAL\u003C/code> and \u003Ccode>NUMERIC\u003C/code> are exact fixed-point base-10 types, while \u003Ccode>FLOAT\u003C/code> and \u003Ccode>DOUBLE PRECISION\u003C/code> are binary approximations. \u003Ccode>DECIMAL\u003C/code> is a safe way to store money; a binary float is not.\u003C/p>\u003C/div>\u003C/div>\u003Cdiv class=\"faq-item\">\u003Cdiv class=\"faq-q\">Why does Stripe send 1000 for ten dollars?\u003C/div>\u003Cdiv class=\"faq-a\">\u003Cp>Because it represents every amount in the currency’s smallest unit. Ten dollars is 1000 cents; ten yen is 10, because JPY is a zero-decimal currency with no minor unit.\u003C/p>\u003C/div>\u003C/div>\u003Cdiv class=\"faq-item\">\u003Cdiv class=\"faq-q\">What about currencies with three decimal places, like the Kuwaiti dinar?\u003C/div>\u003Cdiv class=\"faq-a\">\u003Cp>Store the correct number of minor units for that currency, which is 1000 per dinar, not 100. Do not hard-code two decimals; look up the currency’s exponent.\u003C/p>\u003C/div>\u003C/div>\u003Cdiv class=\"faq-item\">\u003Cdiv class=\"faq-q\">Can I just round at the end and move on?\u003C/div>\u003Cdiv class=\"faq-a\">\u003Cp>Rounding at the end does not undo a float that was already wrong at every intermediate step, and it silently drops remainders. Allocate the remainder explicitly instead of letting a division swallow it.\u003C/p>\u003C/div>\u003C/div>\u003Cdiv class=\"faq-item\">\u003Cdiv class=\"faq-q\">Integer cents or a money library, which should I pick?\u003C/div>\u003Cdiv class=\"faq-a\">\u003Cp>Either, as long as you are consistent. A first-class exact decimal type wrapped in a money object is fine, and integer minor units are fine. Mixing them, or reaching for a binary float, is what gets you.\u003C/p>\u003C/div>\u003C/div>\u003C/div>","\n## TL;DR\n\nRepresent money as an integer count of the smallest currency unit, 1234 for $12.34, never a binary float. Floats cannot hold most decimal amounts exactly, so they leak fractions of a cent that turn into a settlement that does not reconcile.\n\nStore money as an integer count of the smallest currency unit. 1234 means $12.34. Do not use a binary float, ever, for an amount you will add, compare, or refund. This is not a style preference. It is the difference between books that reconcile and a slow leak you only find when someone audits settlement.\n\n## Why a float cannot hold $2.78\n\nIEEE 754 binary floats store numbers as sums of powers of two. Most decimal fractions, including 0.1 and 2.78, have no exact binary form, so the machine keeps the nearest approximation. The famous demonstration is one line in almost every language:\n\n```js\n0.1 + 0.2            // 0.30000000000000004\n0.1 + 0.2 === 0.3    // false\n```\n\nPHP, Python, and Java doubles do the same thing. The value looks right when you print it with two decimals, which is exactly why this bug survives to production. The moment you add, multiply, or compare these amounts, the tiny error compounds, and as [Modern Treasury explains in their writeup on why floats do not work for cents](https://www.moderntreasury.com/journal/floats-dont-work-for-storing-cents), every workaround (round to two places, compare within an epsilon) is a patch on a representation that was wrong for money from the start.\n\n## Integer minor units, the boring correct default\n\nStore the amount as a whole number of the smallest unit the currency has. $12.34 is 1234. This is what [Stripe](https://docs.stripe.com/currencies) does: every amount in its API is a positive integer in the smallest currency unit, so 1000 charges ten dollars, and 10 charges ten yen, because JPY is a zero-decimal currency with no minor unit. Integers add, subtract, and compare exactly. A database `BIGINT` is cheap, unambiguous, and immune to the epsilon dance.\n\nThe single discipline this asks of you: format to a decimal string only at the very edge, when you render to a human or hand the amount to a gateway. Everywhere in between, it is an integer.\n\n## The rule that actually saves you: never divide a total\n\nThe float question gets the attention, but the bug I see more often is dividing a total to get its parts. Split $10.00 three ways by dividing and you get 333 plus 333 plus 333, which is 999, and a cent has vanished into the floor function. I walked through this exact failure in a payments context in [six payments edge cases that bite at 2am](/blog/six-payments-edge-cases-that-bite-at-2am).\n\nThe fix is to allocate, not divide: give each part the floor, then hand out the remainder one unit at a time until it is gone. Martin Fowler named this decades ago as the [Money pattern's `allocate()`](https://martinfowler.com/eaaCatalog/money.html), which exists precisely so you avoid losing pennies to rounding.\n\n```php\nfunction allocate(int $totalCents, int $parts): array {\n    $base = intdiv($totalCents, $parts);\n    $remainder = $totalCents - $base * $parts;\n    $result = array_fill(0, $parts, $base);\n    for ($i = 0; $i \u003C $remainder; $i++) {\n        $result[$i]++;   // distribute the leftover, one cent at a time\n    }\n    return $result;      // 1000 over 3 -> [334, 333, 333], sums back to 1000\n}\n```\n\nThis is the same discipline that keeps a refund honest and keeps a mid-cycle plan change honest. It is why I read the refund path first when I audit a codebase, in [the refund path is where I read a codebase's soul](/blog/the-refund-path-is-where-i-read-a-codebases-soul), and why proration is where subscription systems quietly leak, in [subscriptions are just licensing that bills on time](/blog/subscriptions-are-licensing-that-bills-on-time).\n\n## The honest counterargument\n\nThere is a reasonable objection, argued well in [Storing money as integer cents is often over-engineering](https://world.hey.com/otar/storing-money-as-integer-cents-is-often-over-engineering-7238a485): integer cents can be more ceremony than a small app needs, and an exact `DECIMAL` column with a money library does the job. I agree with half of it. A SQL `DECIMAL` (or `NUMERIC`) is exact, fixed-point, base-10 arithmetic, not a float, and [every serious database has had it for decades](https://cardinalby.github.io/blog/post/best-practices/storing-currency-values-data-types/). If your stack has a first-class decimal type and you use it consistently, you will not leak money.\n\nWhat I will not accept is a binary float, and I will not accept deriving a part by dividing a whole. So pick one exact representation, integer minor units or a real decimal type, commit to it, and never mix the two in the same column.\n\n## What this looks like in a schema\n\n```sql\n-- good: exact, unambiguous, integer minor units\namount_cents  BIGINT        NOT NULL,   -- 1234 = $12.34\ncurrency      CHAR(3)       NOT NULL,   -- 'USD'\n\n-- also fine, if you commit to it everywhere\namount        NUMERIC(14, 2) NOT NULL,\n\n-- never\namount        DOUBLE PRECISION           -- a slow leak with a two-decimal disguise\n```\n\nStore the currency next to the amount. An amount without its currency is Fowler's other warning: sooner or later you add dollars to yen and nobody notices until the total is wrong.\n\n## The takeaway\n\nMoney is a count, not a measurement, so represent it as an integer of the smallest unit, keep the currency beside it, and never derive a part by dividing a whole. Do that and your refunds, proration, and reconciliation stay honest. Skip it and you ship a leak that hides until the numbers stop matching at settlement, which is the most expensive place to find it.\n\n## FAQ\n\n**Is a SQL DECIMAL just a float under the hood?**\nNo. `DECIMAL` and `NUMERIC` are exact fixed-point base-10 types, while `FLOAT` and `DOUBLE PRECISION` are binary approximations. `DECIMAL` is a safe way to store money; a binary float is not.\n\n**Why does Stripe send 1000 for ten dollars?**\nBecause it represents every amount in the currency's smallest unit. Ten dollars is 1000 cents; ten yen is 10, because JPY is a zero-decimal currency with no minor unit.\n\n**What about currencies with three decimal places, like the Kuwaiti dinar?**\nStore the correct number of minor units for that currency, which is 1000 per dinar, not 100. Do not hard-code two decimals; look up the currency's exponent.\n\n**Can I just round at the end and move on?**\nRounding at the end does not undo a float that was already wrong at every intermediate step, and it silently drops remainders. Allocate the remainder explicitly instead of letting a division swallow it.\n\n**Integer cents or a money library, which should I pick?**\nEither, as long as you are consistent. A first-class exact decimal type wrapped in a money object is fine, and integer minor units are fine. Mixing them, or reaching for a binary float, is what gets you.\n",[21,24,27,30,33],{"q":22,"a":23},"Is a SQL DECIMAL just a float under the hood?","No. DECIMAL and NUMERIC are exact fixed-point base-10 types, while FLOAT and DOUBLE PRECISION are binary approximations. DECIMAL is a safe way to store money; a binary float is not.",{"q":25,"a":26},"Why does Stripe send 1000 for ten dollars?","Because it represents every amount in the currency's smallest unit. Ten dollars is 1000 cents; ten yen is 10, because JPY is a zero-decimal currency with no minor unit.",{"q":28,"a":29},"What about currencies with three decimal places, like the Kuwaiti dinar?","Store the correct number of minor units for that currency, which is 1000 per dinar, not 100. Do not hard-code two decimals; look up the currency's exponent.",{"q":31,"a":32},"Can I just round at the end and move on?","Rounding at the end does not undo a float that was already wrong at every intermediate step, and it silently drops remainders. Allocate the remainder explicitly instead of letting a division swallow it.",{"q":34,"a":35},"Integer cents or a money library, which should I pick?","Either, as long as you are consistent. A first-class exact decimal type wrapped in a money object is fine, and integer minor units are fine. Mixing them, or reaching for a binary float, is what gets you.","6 min read",6,1785070532919]