Summary#
Exact base-10 arithmetic, for money and anything else where a fraction of a cent matters. decimal is not a binary
float: 0.1 + 0.2 is exactly 0.3, and it behaves identically whether your code runs on the server or in the browser.
Signature#
decimal Total = 19.99m; // the `m` suffix makes a literal a decimalDescription#
Use decimal for money, quantities, tax rates, percentages — anything where the answer has to be the answer rather
than a very close one. Use double for measurements and scientific values, where the extra range matters more than the
last digit.
The difference is not cosmetic. A binary float cannot represent 0.1 exactly, so 0.1 + 0.2 lands a hair above 0.3
and 1.005 * 100 lands a hair below 100.50 — which then rounds to 100.49. A decimal stores the digits you
wrote, so it gives the answer you would give.
decimal price = 1.005m;
decimal total = price * 100m; // exactly 100.500 — a double would say 100.49999999999999The same answer everywhere#
An Osy# expression means one thing. A decimal is exact wherever the function runs — server-side, or in-process in
the browser (see execution side). A comparison that is true on one side is true on the other; a total that
prints "1.10" on one side prints "1.10" on the other. You do not need to know, or care, where a piece of code
executes in order to trust its arithmetic.
Rounding is banker's rounding#
Math.Round rounds half to even, not half up. That is deliberate: always rounding .5 upward biases a long column
of figures steadily upward, which is exactly what you do not want in a ledger.
Math.Round(2.5m) // 2 — not 3
Math.Round(3.5m) // 4
Math.Round(1.005m, 2) // 1.00Round explicitly when you present a value. Rounding as you go accumulates error just as surely as a float would.
Trailing zeros are part of the value#
1.10m keeps its two decimal places and prints as "1.10" — a currency total keeps its cents column. But equality is
numeric, so 1.1m == 1.10m is true. Arithmetic follows the same rules you would use on paper: addition takes the
wider of the two scales, and multiplication adds them.
1.10m + 2.20m // 3.30
1.5m * 1.5m // 2.25
1.1m == 1.10m // trueExamples#
class Line {
decimal Price;
int Quantity;
decimal Total() {
return Price * Quantity;
}
}decimal WithTax(decimal subtotal, decimal rate) {
return Math.Round(subtotal * (1m + rate), 2);
}See also#
- execution side — where a function runs; a decimal is exact on either side