Numbers #

Quest provides distinct types for integers and floating-point numbers, each with their own characteristics and behaviors.

Number Trait #

All numeric types (Int, Float, Decimal, and BigInt) implement a common Number trait that provides consistent arithmetic and utility methods. This trait includes:

  • Arithmetic: add, sub, mul, div, mod, pow, abs, neg
  • Rounding: round, floor, ceil, trunc
  • Comparison: eq, neq, lt, lte, gt, gte
  • Utility: sign, min, max

These methods work consistently across all number types, making it easy to write generic numeric code.

Number Types #

Int - 64-bit Signed Integers #

  • Storage: i64 (64-bit signed integer)
  • Range: -9,223,372,036,854,775,807 to 9,223,372,036,854,775,807
  • Overflow: Checked (raises error on overflow)
  • Division: Integer division (truncates)

Float - 64-bit Floating Point #

  • Storage: f64 (64-bit floating-point)
  • Precision: ~15-17 decimal digits
  • Special values: NaN, Infinity, -Infinity
  • Division: Exact floating-point division

Decimal - Arbitrary Precision #

  • Storage: 28-29 significant digits
  • Usage: PostgreSQL NUMERIC/DECIMAL columns, financial calculations
  • See: Decimal documentation

Number Literals #

Decimal Literals #

# Integer literals (create Int)
let age = 42
let negative = -17
let zero = 0
let big = 9223372036854775807

# Float literals (create Float)
let pi = 3.14159
let temp = -2.5
let one = 1.0                    # Float, not Int

# Digit separators for readability
let million = 1_000_000          # Int
let billion = 7_900_000_000      # Int
let precise_pi = 3.141_592_653   # Float

Scientific Notation #

Scientific notation always creates Float type:

# Large numbers
let avogadro = 6.022e23          # 6.022 × 10^23
let light_speed = 2.998e8        # 299,800,000

# Small numbers
let planck = 6.626e-34           # 0.0000000000000000000000000000000006626
let micro = 1.5e-6               # 0.0000015

# Both e and E work
let billion = 1E9                # 1,000,000,000
let positive = 1e+10             # Explicit + sign allowed

# With digit separators
let avogadro_precise = 6.022_140_76e23

Binary Literals #

Binary literals (prefix 0b or 0B) create Int type:

# Basic binary
let flags = 0b1010               # 10
let byte = 0b11111111            # 255

# With digit separators (grouped by nibbles or bytes)
let mask = 0b1111_0000           # 240
let word = 0b1111_0000_1010_0101 # 61605

# Uppercase B works too
let value = 0B1111               # 15

# Use cases: bit flags, permissions, masks
let READ = 0b001                 # 1
let WRITE = 0b010                # 2
let EXECUTE = 0b100              # 4
let RWX = 0b111                  # 7

Hexadecimal Literals #

Hexadecimal literals (prefix 0x or 0X) create Int type:

# Basic hex
let color = 0xFF                 # 255
let max = 0xFFFF                 # 65535

# Case-insensitive digits
let same1 = 0xFF                 # 255
let same2 = 0xff                 # 255
let same3 = 0xFf                 # 255

# With digit separators (grouped by bytes)
let red = 0xFF_00_00             # 16711680 (RGB color)
let uuid = 0xDEAD_BEEF           # 3735928559

# Uppercase X works too
let value = 0XFF                 # 255

# Use cases: colors, memory addresses, constants
let white = 0xFFFFFF             # 16777215
let black = 0x000000             # 0
let addr = 0x1000                # 4096

Octal Literals #

Octal literals (prefix 0o or 0O) create Int type:

# Basic octal
let perms = 0o755                # 493 (rwxr-xr-x)
let umask = 0o022                # 18

# With digit separators
let multi = 0o755_644            # 252836

# Uppercase O works too
let value = 0O777                # 511

# Use cases: Unix file permissions
let rwxrwxrwx = 0o777            # 511 (full permissions)
let rw_r__r__ = 0o644            # 420 (read/write for owner, read for others)
let rwxr_xr_x = 0o755            # 493 (executable for owner, readable for others)

Digit Separators #

Use underscores (_) as visual separators in any numeric literal:

# Integers
let million = 1_000_000
let billion = 1_000_000_000
let card_number = 1234_5678_9012_3456

# Floats
let pi = 3.141_592_653_589_793
let e = 2.718_281_828_459_045

# Scientific notation
let avogadro = 6.022_140_76e23
let in_exponent = 1e1_00         # Underscores in exponent too

# Binary (group by 4 or 8)
let ipv4 = 0b11000000_10101000_00000001_00000001

# Hex (group by 2 or 4)
let color = 0xFF_80_00
let id = 0x550e_8400_e29b

# Octal (group by 3)
let perms = 0o7_5_5

# Rules:
# - Can appear anywhere between digits
# - Cannot start or end the number
# - Cannot appear before/after decimal point (use 1_000.5 not 1_000_.5)
# - Purely visual - 1_000 and 1000 are identical

Type Behavior #

Type Preservation #

Arithmetic operations preserve types when possible:

let a = 5        # Int
let b = 10       # Int
let c = a + b    # Int (15)

let x = 3.14     # Float
let y = 2.5      # Float
let z = x + y    # Float (5.64)

Type Promotion #

Mixed operations promote to the more general type:

let int_val = 5          # Int
let float_val = 2.5      # Float

let result = int_val + float_val   # Float (7.5)

Promotion rules:

  • Int + Int = Int
  • Int + Float = Float
  • Float + Float = Float
  • Float + Decimal = Decimal

Integer Division #

Integer division truncates toward zero:

puts(10 / 3)      # 3 (Int)
puts(10 / 4)      # 2 (Int)
puts(-10 / 3)     # -3 (Int)

puts(10.0 / 3.0)  # 3.3333... (Float)

Type Conversion #

Int Methods #

let num = 42
puts(num.to_f64())       # Convert to Float: 42.0
puts(num.to_string())    # Convert to String: "42"

Float Methods #

let pi = 3.14159
puts(pi.to_int())        # Convert to Int: 3 (truncates)
puts(pi.floor())         # Round down to Int: 3
puts(pi.ceil())          # Round up to Int: 4
puts(pi.round())         # Round to nearest Int: 3
puts(pi.to_string())     # Convert to String: "3.14159"

To Boolean #

# Zero is falsy, all other numbers are truthy
if 0
    puts("won't print")
end

if 42
    puts("will print")      # prints
end

if -5
    puts("negative is truthy")  # prints
end

Arithmetic Operations #

Quest supports standard arithmetic operators:

let a = 10
let b = 3

puts(a + b)   # 13 - addition
puts(a - b)   # 7  - subtraction
puts(a * b)   # 30 - multiplication
puts(a / b)   # 3  - division (integer division for Int)
puts(a % b)   # 1  - modulo/remainder

# Unary operators
puts(-a)      # -10 - negation
puts(+a)      # 10  - unary plus (no-op)

Overflow Detection (Int only) #

Integer operations check for overflow:

let big = 9223372036854775807  # i64::MAX
try
    let overflow = big + 1
catch e
    puts("Integer overflow detected!")
end

Comparison Operations #

let x = 10
let y = 20

puts(x == y)  # false - equality
puts(x != y)  # true  - inequality
puts(x < y)   # true  - less than
puts(x > y)   # false - greater than
puts(x <= y)  # true  - less than or equal
puts(x >= y)  # false - greater than or equal

# Mixed type comparisons work
puts(10 == 10.0)    # true
puts(5 < 5.5)       # true

Int Methods #

Arithmetic Methods #

All arithmetic operations available as methods:

let a = 10
let b = 3

# Original method names
puts(a.plus(b))    # 13
puts(a.minus(b))   # 7
puts(a.times(b))   # 30
puts(a.div(b))     # 3
puts(a.mod(b))     # 1

# Number trait aliases (more concise)
puts(a.add(b))     # 13 - alias for plus
puts(a.sub(b))     # 7  - alias for minus
puts(a.mul(b))     # 30 - alias for times

# Power operation
puts(2.pow(3))     # 8 - 2^3
puts(5.pow(2))     # 25 - 5^2

# Negation
puts(a.neg())      # -10 - negation

Comparison Methods #

puts(5.eq(5))      # true
puts(5.neq(3))     # true
puts(10.gt(5))     # true
puts(3.lt(7))      # true
puts(10.gte(5))    # true
puts(3.lte(7))     # true

Utility Methods #

let num = -42
puts(num.abs())         # 42 - absolute value
puts(num.neg())         # 42 - negation (same as -num)
puts(num.sign())        # -1 - sign indicator (-1, 0, or 1)

# Min/max
puts(10.min(20))        # 10 - minimum
puts(10.max(5))         # 10 - maximum

# Rounding (identity operations for Int)
puts(num.round())       # -42 - already an integer
puts(num.floor())       # -42 - already an integer
puts(num.ceil())        # -42 - already an integer
puts(num.trunc())       # -42 - already an integer

Conversion Methods #

let num = 42
puts(num.to_f64())      # 42.0 - convert to Float
puts(num.to_string())   # "42" - convert to String
puts(num.cls())         # "Int" - type name
puts(num._id())         # unique object ID

Float Methods #

Arithmetic Methods #

let x = 3.14
let y = 2.5

# Original method names
puts(x.plus(y))    # 5.64
puts(x.minus(y))   # 0.64
puts(x.times(y))   # 7.85
puts(x.div(y))     # 1.256
puts(x.mod(y))     # 0.64

# Number trait aliases (more concise)
puts(x.add(y))     # 5.64 - alias for plus
puts(x.sub(y))     # 0.64 - alias for minus
puts(x.mul(y))     # 7.85 - alias for times

# Power operation
puts(2.0.pow(3.0)) # 8.0 - 2^3
puts(x.pow(2.0))   # 9.8596 - x^2

# Negation
puts(x.neg())      # -3.14 - negation

Rounding Methods #

let value = 3.7

puts(value.floor())     # 3 - round down (returns Float)
puts(value.ceil())      # 4 - round up (returns Float)
puts(value.round())     # 4 - round to nearest (returns Float)
puts(value.trunc())     # 3 - truncate decimal part (returns Float)

Utility Methods #

let pi = 3.14159

# Absolute value and sign
puts(pi.abs())          # 3.14159 - absolute value
puts(pi.sign())         # 1.0 - sign indicator (-1.0, 0.0, or 1.0)
puts((-pi).sign())      # -1.0

# Min/max
puts(pi.min(3.0))       # 3.0 - minimum
puts(pi.max(4.0))       # 4.0 - maximum

Special Value Checks #

let x = 3.14
let nan = 0.0 / 0.0
let inf = 1.0 / 0.0

puts(x.is_nan())        # false
puts(x.is_infinite())   # false
puts(x.is_finite())     # true

puts(nan.is_nan())      # true
puts(inf.is_infinite()) # true

Conversion Methods #

let pi = 3.14159
puts(pi.to_int())       # 3 - convert to Int (truncates)
puts(pi.to_string())    # "3.14159" - convert to String
puts(pi.cls())          # "Float" - type name

Mathematical Operations #

For advanced mathematical operations, use the std/math module:

use "std/math" as math

puts(math.sin(math.pi / 2))    # 1.0 - sine
puts(math.cos(0))               # 1.0 - cosine
puts(math.sqrt(16.0))           # 4.0 - square root
puts(math.pow(2.0, 3.0))        # 8.0 - power
puts(math.floor(3.7))           # 3.0 - round down
puts(math.ceil(3.2))            # 4.0 - round up
puts(math.round(3.5))           # 4.0 - round to nearest

See the math module documentation for the complete list of mathematical functions.

Display Formatting #

Int Display #

Integers always display without decimal point:

puts(42)       # 42
puts(-17)      # -17
puts(0)        # 0

Float Display #

Floats display as integers when the fractional part is zero and value is small enough:

puts(42.0)     # 42 (displays as integer)
puts(42.5)     # 42.5
puts(3.14)     # 3.14
puts(1.0e10)   # 10000000000
puts(1.0e11)   # 1e11 (scientific notation)

Display rule: A float displays as an integer if:

  • Its fractional part is zero (value.fract() == 0.0)
  • AND its absolute value is less than 10 billion (value.abs() < 1e10)

Ranges #

Numbers work with ranges for iteration:

# Iterate from 1 to 5 (inclusive)
for i in 1 to 5
    puts(i)
end
# Output: 1, 2, 3, 4, 5

# Iterate from 0 to 9 (exclusive)
for i in 0 until 10
    puts(i)
end
# Output: 0, 1, 2, ..., 9

# With step
for i in 0 to 10 step 2
    puts(i)
end
# Output: 0, 2, 4, 6, 8, 10

Common Patterns #

Check if Even/Odd #

let num = 42
if num % 2 == 0
    puts("even")
else
    puts("odd")
end

Absolute Value #

let value = -42
puts(value.abs())  # 42

Min/Max #

let a = 10
let b = 20
let min = if a < b then a else b
let max = if a > b then a else b

Clamping #

# Clamp value between min and max
let value = 150
let min = 0
let max = 100
let clamped = if value < min
    min
elif value > max
    max
else
    value
end
puts(clamped)  # 100

Type Checking #

let x = 42
let y = 3.14

puts(x.cls())   # "Int"
puts(y.cls())   # "Float"

# Check type at runtime
if x.cls() == "Int"
    puts("x is an integer")
end

Notes #

  • Int operations check for overflow and raise errors
  • Float operations follow IEEE 754 floating-point standard
  • Integer division truncates: 10 / 3 = 3
  • Float division is exact: 10.0 / 3.0 = 3.333...
  • Division by zero raises an error for both types
  • All arithmetic and comparison operators have equivalent methods
  • Use the std/math module for advanced mathematical operations
  • Numbers are immutable - operations return new number values
  • Mixed-type operations automatically promote to the more general type