How do Integer and Float work in EK9?
← Getting Started · Ref: Q39
EK9 has two numeric types: Integer (64-bit signed whole numbers) and Float (64-bit IEEE 754 floating-point). Both are full objects, not primitives.
INTEGER LITERALS
Decimal: 42, -17, 0
Hex: 0xFF, 0x1F (prefix 0x)
Binary: 0b1010, 0b11001100 (prefix 0b)
Underscores for readability: 1_000_000, 0xFF_FF
Range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
FLOAT LITERALS
Decimal: 3.14, -2.5, 0.0
Scientific notation: 4.9E-324, 1.7976931348623157E308
ARITHMETIC OPERATORS
Both types support: + - * / ^ (power), abs, sqrt
17 + 5 = 22, 17 / 5 = 3 (integer division) 3.14 * 2.0 = 6.28, 2.0 ^ 0.5 = square root of 2
DIVISION BY ZERO RETURNS UNSET
This is one of EK9's most important safety decisions. Java throws ArithmeticException for integer division by zero and produces NaN/Infinity for float. JavaScript silently produces Infinity or NaN that propagates through calculations. EK9 returns unset for ANY division by zero:
90 / 0 is unset, 0 / 0 is unset, 90.0 / 0.0 is unset
But 0 / 90 = 0 (zero divided by non-zero is valid).
IEEE 754 NaN was designed to let calculations continue, hoping programmers would check later. 50 years shows they don't. NaN propagates silently producing garbage, and NaN != NaN breaks equality. EK9's unset achieves the same 'continue calculating' benefit but the ? operator makes checking explicit.
PROMOTE INTEGER TO FLOAT: #^
Use the #^ operator to convert Integer to Float:
intVal <- 42 floatVal <- #^ intVal
Float has no promote because it is already the widest numeric type. This explicit promotion means you always know where Integer becomes Float.
WHY ONLY TWO NUMERIC TYPES?
Java has 6 numeric primitives plus boxed wrappers. Rust has 12 numeric types. Go has 15+. EK9 has Integer and Float. Period. No byte, short, long, unsigned, int8, float32.
This eliminates: silent narrowing conversions (Java long to int), signed/unsigned confusion (C unsigned overflow), float-to-double precision surprises, integer overflow wrapping. One whole number type, one decimal type. If you need arbitrary precision decimals, use Money. If you need bit manipulation, use Bits.
See Q91 for advanced features (mod vs rem, factorial, bitwise, locale formatting, streams). See Q40 for the Bits type. See Q25 for more on the #^ promote operator. See Q44 for Locale formatting. See Q240 for arithmetic and math operators (mod, rem, abs, sqrt, ^). Use 'ek9 -h Integer' and 'ek9 -h Float' for the full API.
Example
defines module qa.integer.and.float defines program IntegerAndFloatDemo() stdout <- Stdout() // === INTEGER LITERALS === decimal <- 42 hex <- 0xFF binary <- 0b10101010 big <- 1_000_000 stdout.println(`Decimal: ${decimal}`) stdout.println(`Hex 0xFF: ${hex}`) stdout.println(`Binary 0b10101010: ${binary}`) stdout.println(`With underscores: ${big}`) // === BASIC ARITHMETIC === a <- 17 b <- 5 stdout.println(`${a} + ${b} = ${a + b}`) stdout.println(`${a} - ${b} = ${a - b}`) stdout.println(`${a} * ${b} = ${a * b}`) stdout.println(`${a} / ${b} = ${a / b}`) // Power, absolute value, square root stdout.println(`2 ^ 10 = ${2 ^ 10}`) stdout.println(`abs -49 = ${abs -49}`) stdout.println(`sqrt 49 = ${sqrt 49}`) // === FLOAT BASICS === pi <- 3.14159 scientific <- 2.5E10 stdout.println(`Pi: ${pi}`) stdout.println(`Scientific: ${scientific}`) stdout.println(`2.0 ^ 0.5 = ${2.0 ^ 0.5}`) stdout.println(`abs -16.0 = ${abs -16.0}`) // === DIVISION BY ZERO RETURNS UNSET === zero <- 0 divResult <- 90 / zero stdout.println(`90 / 0 isSet: ${divResult?}`) fzero <- 0.0 fResult <- 90.0 / fzero stdout.println(`90.0 / 0.0 isSet: ${fResult?}`) // Zero divided by non-zero is valid normalDiv <- 0 / 90 stdout.println(`0 / 90 = ${normalDiv}`) // === PROMOTE INTEGER TO FLOAT === intVal <- 42 floatVal <- #^ intVal stdout.println(`Promoted: ${floatVal}`) // === COMPARISON === five <- 5 anotherFive <- 5 ten <- 10 stdout.println(`5 < 10: ${five < ten}`) stdout.println(`5 == 5: ${five == anotherFive}`) ordering <- five <=> ten stdout.println(`5 <=> 10: ${ordering}`)
Common mistakes
E50030 — A String value cannot be assigned to an Integer variable. EK9 is strongly typed and does not implicitly convert between unrelated types. Use Integer("42") for explicit parsing. See ek9 -h E50030 for details.
Incorrect:
decimal as Integer: "forty-two"
Correct:
decimal <- 42
Other ways to ask this
- What numeric types does EK9 have and how do hex and binary literals work?
- Why does division by zero return unset instead of throwing an exception?
- Why does EK9 only have two numeric types instead of byte, short, long, unsigned?
Coming from another language?
Java: 6 numeric primitives (byte, short, int, long, float, double) plus boxed wrappers, ArithmeticException on integer div-by-zero, Double.NaN for float div-by-zero. Python: arbitrary precision integers, float backed by C double, ZeroDivisionError. JavaScript: single Number type (IEEE 754 double), Infinity/NaN, no integer type. Rust: 12 numeric types (i8-i128, u8-u128, f32, f64), panic on overflow in debug. Go: 15+ numeric types, panic on integer div-by-zero, math.NaN for float. EK9: Integer (64-bit) and Float (64-bit) only, div-by-zero returns unset, explicit #^ promote.
Keywords: beginner, arithmetic, start, basics, binary, unset, zero, float, promote, integer, intro, hex, underscore, number, first, literal, migrate, nan, division, numeric