# Numeric types & literal suffixes

> The platform numeric types are int, long, decimal, and double. Literals follow C# exactly, suffixes (L, m, d) included: a bare decimal-point literal is a DOUBLE, and money is written 19.99m. There is no float: 2.5f is a pointed compile error.

<!-- id: function-numeric-literals · area: function · stability: stable · html: https://osysharp.com/reference/function/numeric-literals/ -->

## Summary        {#summary}
The platform numeric types are `int` (Int32), `long` (Int64), `decimal`, and `double`. Numeric literals
follow C#, with no divergence at all: a bare integer is `int`, a bare decimal-point literal (`2.5`) is a
**`double`**, `L` makes a `long`, `m` a `decimal`, `d` a `double`. There is no `float` type; `2.5f` is a
pointed compile error.

⚠ **This page said the opposite until 2026-08-31**, describing a bare `2.5` as a decimal "money-safe
default". That divergence was real and was **removed on 2026-08-12**; the page was not updated with it, so
it taught a spelling the compiler rejects — `decimal Price() { return 19.99; }` does not compile.

## Signature      {#signature}
```osy syntax
5              // int
5000000000L    // long  (values past int.MaxValue need the suffix)
2.5            // double — same as C#, and same as every other unsuffixed fractional literal
2.5m           // decimal — what money is written as
2.5d           // double
```

## Description    {#description}
- **No `float`.** The platform numeric model has no float type; `2.5f` refuses with
  `float literals … are not supported — the platform numeric types are int/long/decimal/double`.
- **Bare `2.5` is a `double`, exactly as in C#.** It was a decimal until 2026-08-12, and that was the one
  place Osy#'s literals disagreed with the language they mirror — a disagreement invisible until arithmetic
  overflows. A canvas whose fields were all `double` ran entirely in decimal because every literal
  initialising them was one, and threw "value was either too large or too small for a Decimal" naming a type
  the source never wrote.
- **Money is safe BECAUSE of this, not despite it.** C#'s rule is a PAIR: `1.0` is a double AND there is no
  implicit `double`→`decimal`. Osy# keeps both halves, so `decimal price = 19.99;` is a compile error and the
  author writes `19.99m` — strictly safer than a default that made every literal a decimal whether the
  surrounding expression wanted one or not.
- **Arithmetic runs in the decimal lattice** (matching Postgres): a `double` operand behaves like any
  non-integral number — `5d / 2 == 2.5` (a double defeats integer truncation, as in C#), while
  int÷int truncates (`5 / 2 == 2`, SQL parity).
- **Round-trip:** suffixed literals persist as typed literal nodes and decompile ALWAYS suffixed
  (`5000000000L`, `5d`), so the canonical form re-lexes identically.
- **Durable:** across a suspend/resume, a `double` local resumes as `decimal` (the codec's numeric
  normalization — the same value under the decimal arithmetic lattice).
- `const` composes: `const double factor = 1.5d;` folds and inlines like every const.

## Examples       {#examples}
```osy title="suffixes" test app=numeric-literals
long Big() { return 5000000000L; }      // past int.MaxValue — needs the L
double Half() { return 5d / 2; }        // == 2.5 — the double defeats integer truncation
decimal Price() { return 19.99m; }      // money takes the m — a bare 19.99 is a double and will not convert
double Rate() { const double f = 1.5d; return f; }
```

## See also       {#see-also}
- [Typed locals](https://osysharp.com/reference/function/typed-locals/) — declared types + the constant conversion (`double h = 2.5;` works)
- [const](https://osysharp.com/reference/function/const/) — const folding and inlining
