Summary#
A 64-bit whole number, exact across its entire range — roughly ±9.2 quintillion. Use it for values that are counted or
issued rather than measured: ids, sequence numbers, row versions, byte offsets. Like every other value in Osy#, a
long holds the same value wherever the code runs.
Signature#
long Sequence = 9007199254740993;
int Count = 42; // `int` is 32-bit — plenty for a countDescription#
Reach for long when a number is an identity or a position, not a measurement. An id issued by a sequence, a
version stamp, an offset into a file — these are values where being off by one is not a small error, it is the wrong
record. int covers ordinary counts and indexes; use long when the range could plausibly exceed about two billion,
or when the value comes from a system that issues 64-bit ids.
Division truncates#
Whole-number division discards the remainder rather than rounding — 7 / 2 is 3, and -7 / 2 is -3. The remainder
operator takes the sign of the left operand, so -7 % 2 is -1.
7 / 2 // 3 — not 3.5, and not 4
-7 / 2 // -3 — truncated toward zero
-7 % 2 // -1 — the sign follows the dividendThis holds anywhere in an expression, not only when the two operands are written side by side. A whole-number expression stays a whole-number expression however many steps it takes to get there, so a division at the end of a chain truncates exactly as a direct one does.
(3 - 1) / 3 // 0 — the subtraction is still whole-number arithmetic
(9 - 1) / 3 // 2
count * 2 / 3 // truncates; every operand is a whole numberIf you want the fractional answer, ask for one: use decimal (see decimal) or double for the operand.
One fractional operand makes the whole expression fractional, wherever it appears in the chain.
7m / 2m // 3.5
(3 - 1) / 3m // 0.666… — the decimal operand wins the expressionArithmetic fails at the edges rather than wrapping#
A long has a fixed width, so there are values arithmetic on it cannot produce. Running past the maximum raises,
naming the operands and the range — it does not wrap around to the minimum, and it does not quietly grow into a wider
type. Negating the minimum value raises for the same reason: its positive counterpart does not exist in 64 bits.
This is a deliberate difference from C#, which wraps by default. A wrapped total is not an obviously broken value like a blank or an error — it is a plausible number of the wrong sign, and nothing downstream can tell it from a right one. The same expression pushed down into the database raises too, so you get one answer wherever it runs.
If a value can legitimately grow past a long, say so in the type: use decimal (see decimal) or double.
The same answer everywhere#
An Osy# expression means one thing. A long is exact wherever the function runs — server-side, or in-process in
the browser (see execution side) — including for values above 2^53, where a floating-point number would
silently round to a nearby value. Two ids that differ only in their last digit stay two different ids on both sides,
and a comparison that is true on one side is true on the other.
This matters more than it sounds. A rounded id is not an obviously broken value like a blank or an error; it is a perfectly plausible id belonging to a different row. You do not need to know, or care, where a piece of code executes in order to trust that the id you are holding is the one you were given.
Examples#
entity Event {
long ExternalId;
[MaxLength(100)] string Name;
}long PageOffset(long pageIndex, long pageSize) {
return pageIndex * pageSize;
}decimal AveragePerItem(decimal total, long items) {
return items == 0 ? 0m : total / items;
}See also#
- decimal — exact base-10 arithmetic, for money and anything with a fractional part
- execution side — where a function runs; a
longis exact on either side