Integer Literals¶
# Decimal integers
x = 0
y = 42
z = -10
large = 1000000
# Underscores for readability (optional)
million = 1_000_000
billion = 1_000_000_000
Leading zeros are digits (C#'s rule): 007 is the decimal integer 7 and 0_1 is 1. Python refuses
both (leading zeros in decimal integer literals are not permitted); an octal literal is spelled
0o7 in both languages (deviation leading-zero-decimal-integer in docs/deviations.yaml).
Type Inference¶
- Integer literals without a suffix are inferred as
int(32-bit,System.Int32) by default - If the literal value exceeds
Int32.MaxValue(2,147,483,647), it is automatically promoted tolong(64-bit,System.Int64) - If the literal value exceeds
Int64.MaxValue(9,223,372,036,854,775,807), it is a compile-time error - Suffix notation for explicit sizing (optional):
Lorlforint64(System.Int64):42LuorUforuint32(System.UInt32):42uulorULforuint64(System.UInt64):42ul
The integer suffixes work on every base — hexadecimal (0x), binary (0b) and octal (0o)
literals take them exactly as decimal ones do, with or without digit separators:
def main():
mult = 0x41C64E6Du # uint32
mask = 0xFFFF_FFFFL # int64
flags = 0b1010_0101u # uint32
mode = 0o755ul # uint64
print(mult, mask, flags, mode) # 1103515245 4294967295 165 493
A hex digit is never read as a suffix: a–f and A–F are digits, so 0xFFf is 0xFFF
(4095) and 0x1D is 29. The float suffixes f, d and m (see
Float Literals) apply to decimal literals only; on a binary or octal literal,
and m on a hex literal, they are refused (SPY0023), as in C#:
# x = 0b11f # ERROR (SPY0023): Invalid numeric suffix: f (a binary literal accepts only the integer suffixes u, l and ul)
# y = 0xFFm # ERROR (SPY0023): Invalid numeric suffix: m (a hexadecimal literal accepts only the integer suffixes u, l and ul)
Note: Like C#, there are no literal suffixes for int16, uint16, uint8, or int8. Use type annotations or explicit casts:
# Type annotation
s: int16 = 42
b: uint8 = 255
sb: int8 = -128
# Explicit casting (with the `as!` operator)
s = 42 as! int16
b = 255 as! uint8
Overflow Promotion¶
When an integer literal exceeds the range of int (32-bit), the compiler automatically promotes it to the next wider type:
| Value Range | Inferred Type |
|---|---|
| -2,147,483,648 to 2,147,483,647 | int (System.Int32) |
Outside int range |
int64 (System.Int64) |
small = 42 # int (fits in 32-bit)
large = 3_000_000_000 # int64 (exceeds int range, auto-promoted)
explicit = 42L # int64 (explicit suffix)
Literals in a mixed-type expression¶
A suffixed literal carries its own type: 5u is uint32 and 5ul is uint64, exactly as a
variable of that type would be. An unsuffixed literal too large for int is int64.
# a: int32 = 5u # ERROR (SPY0220): Cannot assign type 'uint32' to variable of type 'int32'
# b: int32 = 4294967296 # ERROR (SPY0220): Cannot assign type 'int64' to variable of type 'int32'
An unsuffixed literal converts to an unsigned type by its value, wherever it is stored — an
annotated variable, an argument, a return, a const — as C#'s uint m = 0xFFFFFFFF; does. Only a
literal does: a suffixed or declared int64 constant converts to uint64 alone (C# §10.2.11).
mask: uint32 = 0xFFFFFFFF # 4294967295 — the literal's value fits uint32
top: uint32 = 2147483648 # 2147483648
print(mask, top)
# big: uint32 = 4294967296 # ERROR (SPY0220): Cannot assign type 'int64' to variable of type 'uint32'
# n: uint8 = 5L # ERROR (SPY0220): Cannot assign type 'int64' to variable of type 'uint8'
When such a literal meets an operand of another type, the pair goes through C#'s binary numeric
promotion (§12.4.7) — the same table +, < and & all use, tabulated in
Numeric Type Promotion. A uint64 operand
against any signed operand has no common type and is refused (SPY0222).
A constant operand converts first. When exactly one operand is an integer constant whose value fits the other operand's type, the constant converts to that type before promotion (C# §10.2.11), so the operator sees a same-type pair and the result keeps the variable's type:
b: uint32 = 5
c: uint32 = b + 1 # 6 — the constant 1 converts to uint32; the result is uint32
u: uint64 = 5
s: uint64 = u + 4294967296 # 4294967301 — an int64-typed constant that fits uint64 converts too
a: uint64 = 5
# print(a + (-1)) # ERROR (SPY0222): Type 'uint64' does not support operator '+'
# # with operand of type 'int32' — -1 has no uint64 value
The constant's own recorded type does not change (hover on 1 still shows int); only the
operator's effective operand types do. A constant whose value does not fit stays its own type and
the ordinary promotion applies, so uint32 + 4294967296 is int64.
Implementation - ✅ Native - Direct mapping to C# integer literals.