# decimal

> Exact base-10 arithmetic, for money and anything else where a fraction of a cent matters. It behaves identically whether your code runs on the server or in the browser.

<!-- id: types-decimal · area: types · stability: stable · html: https://osysharp.com/reference/types/decimal/ -->

## Summary        {#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      {#signature}

```osy syntax
decimal Total = 19.99m;      // the `m` suffix makes a literal a decimal
```

## Description    {#description}

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.

```osy title="why a double gets money wrong" syntax
decimal price = 1.005m;
decimal total = price * 100m;      // exactly 100.500 — a double would say 100.49999999999999
```

### The same answer everywhere   {#same-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](https://osysharp.com/reference/function/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   {#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.

```osy title="Math.Round goes half to EVEN, not half up" syntax
Math.Round(2.5m)    // 2   — not 3
Math.Round(3.5m)    // 4
Math.Round(1.005m, 2)   // 1.00
```

Round 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   {#trailing-zeros}

`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.

```osy title="trailing zeros survive, but equality is numeric" syntax
1.10m + 2.20m       // 3.30
1.5m * 1.5m         // 2.25
1.1m == 1.10m       // true
```

## Examples       {#examples}

```osy title="a line total is exact" test app=decimal-basics
class Line {
  decimal Price;
  int Quantity;

  decimal Total() {
    return Price * Quantity;
  }
}
```

```osy title="round once, at the end" test app=decimal-basics
decimal WithTax(decimal subtotal, decimal rate) {
  return Math.Round(subtotal * (1m + rate), 2);
}
```

## See also       {#see-also}
- [execution side](https://osysharp.com/reference/function/execution-side/) — where a function runs; a decimal is exact on either side
