What advanced features do Integer and Float support?
← Getting Started · Ref: Q91
Beyond basic arithmetic (see Q39), EK9 Integer and Float support mod/rem, factorial, bitwise logic, increment/decrement, mixed-type operations, unset propagation, locale formatting, and stream pipelines.
MOD VS REM
Both give the remainder after division, but differ for negative numbers. mod always returns a non-negative result (mathematical modulo). rem preserves the sign of the dividend (like Java %).
17 mod 5 = 2, 17 rem 5 = 2 (identical for positives) -17 mod 5 = 3, -17 rem 5 = -2 (differ for negatives)
mod is useful for wrapping (clock arithmetic, array indexing). rem matches C/Java % behaviour.
FACTORIAL
The ! operator computes factorial:
five <- 5 five! returns 120 (5 * 4 * 3 * 2 * 1)
BITWISE: AND, OR, XOR
Integer supports bitwise logic operators:
0b11001100 and 0b10101010 = 0b10001000 0b11001100 or 0b10101010 = 0b11101110 0b11001100 xor 0b10101010 = 0b01100110
IMPORTANT: Integer does NOT have shift operators. Use the Bits type for shifting, rotation, and variable-width bit sequences. Convert with Bits(integerValue).
INCREMENT AND DECREMENT
Both Integer and Float support ++ and --:
counter <- 10 counter++ gives 11, counter-- gives 10 fVal <- 1.5 fVal++ gives 2.5, fVal-- gives 1.5
MIXED FLOAT AND INTEGER OPERATIONS
Float operators accept both Float and Integer arguments:
3.5 * 4 gives Float 14.0, 3.5 + 4 gives Float 7.5
Integer operators only accept Integer. To convert Integer to Float, use #^ promote (see Q25).
UNSET PROPAGATION
Operations involving unset values return unset:
unsetInt <- Integer() unsetInt + 1 is unset unsetFloat <- Float() unsetFloat + 1.0 is unset
This follows the tri-state principle (see Q29): operations on unknown values produce unknown results.
LOCALE FORMATTING
Both types format through the Locale type:
Locale("en_GB").format(1234567) gives "1,234,567" Locale("de_DE").format(1234567) gives "1.234.567"
Float adds decimal place control:
Locale("en_GB").format(3.14159, 2) gives "3.14" Locale("de_DE").format(3.14159, 4) gives "3,1416"
See Q44 for the full Locale API.
FOR-RANGE WITH STEP AND STREAM PIPELINE
Integer integrates with for-range and stream pipelines:
theValues <- List() of Integer intSum <- for i in 1 ... 11 by 2 | tee in theValues | collect as Integer
Generates 1, 3, 5, 7, 9, 11 (step by 2), tees each into theValues, collects the sum (36).
See Q39 for Integer and Float basics (literals, arithmetic, division by zero, promote). See Q40 for the Bits type. See Q44 for full Locale formatting. See Q240 for arithmetic and mathematical operators (mod, rem, abs, sqrt, ^).
Example
defines module qa.numeric.advanced defines program NumericAdvancedDemo() stdout <- Stdout() // === MOD VS REM === stdout.println(`17 mod 5 = ${17 mod 5}`) stdout.println(`17 rem 5 = ${17 rem 5}`) stdout.println(`-17 mod 5 = ${-17 mod 5}`) stdout.println(`-17 rem 5 = ${-17 rem 5}`) // === FACTORIAL === five <- 5 stdout.println(`5! = ${five!}`) // === BITWISE LOGIC === x <- 0b11001100 y <- 0b10101010 stdout.println(`x and y: ${x and y}`) stdout.println(`x or y: ${x or y}`) stdout.println(`x xor y: ${x xor y}`) // For shift operations, use the Bits type bitValue <- Bits(x) stdout.println(`Bits of x: ${bitValue}`) // === INCREMENT AND DECREMENT === counter <- 10 counter++ stdout.println(`After ++: ${counter}`) counter-- stdout.println(`After --: ${counter}`) fVal <- 1.5 fVal++ stdout.println(`Float after ++: ${fVal}`) fVal-- stdout.println(`Float after --: ${fVal}`) // === MIXED FLOAT AND INTEGER === product <- 3.5 * 4 sum <- 3.5 + 4 stdout.println(`3.5 * 4 = ${product}`) stdout.println(`3.5 + 4 = ${sum}`) // === UNSET PROPAGATION === unsetInt <- Integer() propagatedInt <- unsetInt + 1 stdout.println(`Unset int + 1 isSet: ${propagatedInt?}`) unsetFloat <- Float() propagatedFloat <- unsetFloat + 1.0 stdout.println(`Unset float + 1.0 isSet: ${propagatedFloat?}`) // === LOCALE FORMATTING === enGB <- Locale("en_GB") deutsch <- Locale("de_DE") largeNum <- 1234567 stdout.println(`GB: ${enGB.format(largeNum)}`) stdout.println(`DE: ${deutsch.format(largeNum)}`) pi <- 3.141592653589793 stdout.println(`GB 2 decimals: ${enGB.format(pi, 2)}`) stdout.println(`DE 4 decimals: ${deutsch.format(pi, 4)}`) // === FOR-RANGE WITH STEP AND STREAM PIPELINE === theValues <- List() of Integer intSum <- for i in 1 ... 11 by 2 | tee in theValues | collect as Integer stdout.println(`Values: ${theValues}`) stdout.println(`Sum: ${intSum}`)
Common mistakes
E50001 — Integer does NOT have shift operators in EK9. Use the Bits type for shifting, rotation, and variable-width bit sequences. Convert with Bits(integerValue). See ek9 -h E50001 for details.
Incorrect:
shifted <- x << 2
Correct:
bitValue <- Bits(x)
Other ways to ask this
- How do mod, rem, and bitwise operators work on Integer in EK9?
- How do I format numbers with locale-specific separators in EK9?
- What happens when I mix Float and Integer in an expression?
Coming from another language?
Java: % operator (rem semantics only), no factorial operator, bitwise &/|/^/<</>>, Integer.parseInt for locale-agnostic formatting, NumberFormat for locale display, IntStream for range pipelines. Python: % (mod semantics), no factorial operator (use math.factorial), bitwise &/|/^/<</>>, locale module. JavaScript: % (rem semantics), no factorial, bitwise &/|/^/<</>>, Intl.NumberFormat for locale. Rust: % (rem), no factorial, bitwise &/|/^/<</>>, no built-in locale formatting. Go: % (rem), no factorial, bitwise &/|/^/<</>>, no built-in locale formatting. EK9: both mod and rem operators, ! factorial, and/or/xor bitwise (shift on Bits type only), Locale.format() built-in, for-range with stream pipeline integration.
Keywords: locale, increment, format, start, propagation, or, first, xor, beginner, mod, rem, intro, unset, stream, advanced, decrement, factorial, and, pipeline, mixed, bitwise, remainder, migrate