Skip to content

Math & Arithmetic

Domain & error policy

Sema's numeric error behavior follows one rule, split by type:

  • Integer division or modulo by zero raises an error. (/ 1 0), (modulo 7 0), and (mod 7 0) all raise (division by zero / modulo by zero). Integers have no infinity or NaN to return, so the failure surfaces where it happens.
  • Floating-point follows IEEE 754 — overflow and undefined real-domain results return inf, -inf, or NaN instead of raising:
sema
(/ 1.0 0)     ; => inf
(/ -1.0 0)    ; => -inf
(/ 0.0 0.0)   ; => NaN
(log 0)       ; => -inf
(log -1)      ; => NaN
(pow 0 0)     ; => 1
(pow 2 -1)    ; => 1/2

This matches the hardware and mainstream numeric languages, so NaN propagates and inf accumulates rather than forcing error handling around every operation. If you need to reject these, test with math/nan? / math/infinite? explicitly.

No integer overflow — Sema has a full numeric tower, so exact integer arithmetic never wraps. A result beyond i64 range promotes to an arbitrary-precision bignum: (+ 9223372036854775807 1)9223372036854775808, (* 1000000000000 1000000000000)1000000000000000000000000. Exact division yields rationals ((/ 1 3)1/3) and (sqrt -1)0+1i. (See ADR #64 and the numeric-tower ADR.)

The Numeric Tower

Sema implements the full R7RS numeric tower, a nested hierarchy of number types:

integer  ⊂  rational  ⊂  real  ⊂  complex

Every integer is a rational, every rational is real, every real is complex — so complex? is true for every number, and integer? is the narrowest test. Two independent axes describe a value: its level in the tower (integer / rational / real / complex) and its exactness (exact vs. inexact).

  • Exact numbers — integers (any size — they promote to bignums instead of overflowing), exact rationals like 1/3, and complex numbers whose parts are both exact — carry no rounding error.
  • Inexact numbers — floats and any complex with a floating-point part — follow IEEE 754.
  • Exactness contagion: an operation is exact only when all its operands are exact; a single float makes the whole result inexact. This is why (/ 1 3) is the exact 1/3 but (/ 1.0 3) is 0.3333….

Literal grammar

The reader parses each tower type directly, and every literal round-trips through print → read:

FormExampleReads as
Rational1/3, 3/6 (→ 1/2)exact ratio, reduced to lowest terms
Complex (rectangular)3+4i, 1.5+2.5i, 3-4ireal + imaginary part
Pure imaginary+i, -i, 2i, -2i0 ± ni
Radix prefix#xFF (255), #o17 (15), #b1010 (10), #d42base 16 / 8 / 2 / 10 integer
Exactness prefix#e1.5 (→ 3/2), #i1/2 (→ 0.5)force exact / inexact
Combined#e#xFF, #x#eFFradix + exactness, either order
Bignum99999999999999999999999999out-of-range integer literals become bignums

See the reader internals for the full grammar.

Basic Arithmetic

+

Add numbers together. Accepts any number of arguments.

sema
(+ 1 2 3)     ; => 6
(+ 10)        ; => 10
(+)           ; => 0

-

Subtract numbers. With one argument, negates. With multiple, subtracts left to right.

sema
(- 10 3)      ; => 7
(- 10 3 2)    ; => 5
(- 5)         ; => -5

*

Multiply numbers together.

sema
(* 4 5)       ; => 20
(* 2 3 4)     ; => 24
(*)           ; => 1

/

Divide numbers. Exact-by-exact division is exact: it yields a reduced rational when the result is not a whole number (so (/ 10 3) is 10/3, not 3.333…), collapsing back to an integer when the denominator reduces to 1. A float operand makes the result a float (inexact contagion). For truncated integer division use quotient (or its math/quotient alias).

sema
(/ 10 2)      ;; => 5
(/ 10 3)      ;; => 10/3
(/ 10 4)      ;; => 5/2
(/ 10.0 3)    ;; => 3.3333333333333335

mod

Modulo. On exact integers this is floored division — the result takes the sign of the divisor, per R7RS — so (mod -7 2) is 1, not -1. (Float operands keep the IEEE truncated %, whose sign follows the dividend.) For the truncated integer counterpart whose sign follows the dividend, use remainder.

sema
(mod 10 3)    ; => 1
(mod 7 2)     ; => 1
(mod -7 2)    ; => 1

Comparison

<

Less than. Supports chaining.

sema
(< 1 2)       ; => #t
(< 1 2 3)     ; => #t
(< 3 2)       ; => #f

>

Greater than.

sema
(> 3 2)       ; => #t
(> 1 2)       ; => #f

<=

Less than or equal.

sema
(<= 1 2)      ; => #t
(<= 2 2)      ; => #t

>=

Greater than or equal.

sema
(>= 3 2)      ; => #t
(>= 2 2)      ; => #t

=

Equality. For numbers this is numeric equality (so (= 1 1.0) is #t); for non-numbers it falls back to structural equality. Unlike < / >, comparing non-numbers does not error.

sema
(= 1 1)           ; => #t
(= 1 1.0)         ; => #t
(= 1 2)           ; => #f
(= "abc" "abc")   ; => #t   (structural, not an error)

Numeric Utilities

abs

Absolute value.

sema
(abs -5)      ; => 5
(abs 3)       ; => 3
(abs -3.14)   ; => 3.14

min

Return the smallest of 1 or more numbers (the no-arg case errors).

sema
(min 1 2 3)   ;; => 1
(min 5)       ;; => 5
(min)         ;; error: Arity error: min expects 1+ args, got 0

max

Return the largest of 1 or more numbers (the no-arg case errors).

sema
(max 1 2 3)   ;; => 3
(max 5)       ;; => 5
(max)         ;; error: Arity error: max expects 1+ args, got 0

pow

Raise a number to a power.

sema
(pow 2 10)    ; => 1024
(pow 3 3)     ; => 27

sqrt

Square root. The square root of an exact perfect square is returned exactly ((sqrt 16) is 4, not 4.0); otherwise the result is an inexact float. The square root of a negative number is complex.

sema
(sqrt 16)     ; => 4
(sqrt 2)      ; => 1.4142135623730951
(sqrt -1)     ; => 0+1i

log

Natural logarithm.

sema
(log 1)       ; => 0.0
(log 100)     ; => 4.605...

floor

Round down toward negative infinity. Exactness-preserving: a float argument rounds to a float (3.73.0), while an exact rational rounds to an exact integer (7/23).

sema
(floor 3.7)   ; => 3.0
(floor -2.3)  ; => -3.0
(floor 7/2)   ; => 3

ceil

Round up toward positive infinity. Exactness-preserving, like floor.

sema
(ceil 3.2)    ; => 4.0
(ceil -2.7)   ; => -2.0
(ceil 7/2)    ; => 4

round

Round to the nearest integer, ties to even (banker's rounding). Exactness-preserving: a float rounds to a float, an exact rational to an exact integer.

sema
(round 3.5)   ; => 4.0
(round 3.4)   ; => 3.0
(round 7/2)   ; => 4

math/round-to

Round to places decimal places, returning a float (where round only rounds to a whole integer).

sema
(math/round-to 3.14159 2)   ; => 3.14
(math/round-to 0.46666 3)   ; => 0.467

math/format-fixed

Format a number as a fixed-decimal string, padding trailing zeros to places digits — for money/metrics display where math/round-to (a float, which drops trailing zeros) isn't enough.

sema
(math/format-fixed 1.2 3)     ; => "1.200"
(math/format-fixed 3.14159 2) ; => "3.14"

Trigonometry

sin

Sine (argument in radians).

sema
(sin 0)       ; => 0.0
(sin pi)      ; => ~0.0

cos

Cosine (argument in radians).

sema
(cos 0)       ; => 1.0
(cos pi)      ; => -1.0

math/tan

Tangent (argument in radians).

sema
(math/tan 0)       ; => 0.0
(math/tan (/ pi 4)); => ~1.0

math/asin

Inverse sine. Returns radians.

sema
(math/asin 1)      ; => ~1.5707 (π/2)
(math/asin 0)      ; => 0.0

math/acos

Inverse cosine. Returns radians.

sema
(math/acos 0)      ; => ~1.5707 (π/2)
(math/acos 1)      ; => 0.0

math/atan

Inverse tangent. Returns radians.

sema
(math/atan 1)      ; => ~0.7854 (π/4)
(math/atan 0)      ; => 0.0

math/atan2

Two-argument inverse tangent. Returns the angle in radians between the positive x-axis and the point (x, y).

sema
(math/atan2 1 1)   ; => ~0.7854 (π/4)
(math/atan2 0 -1)  ; => ~3.1416 (π)

Hyperbolic Functions

math/sinh

Hyperbolic sine.

sema
(math/sinh 0)      ; => 0.0
(math/sinh 1)      ; => 1.1752...

math/cosh

Hyperbolic cosine.

sema
(math/cosh 0)      ; => 1.0
(math/cosh 1)      ; => 1.5430...

math/tanh

Hyperbolic tangent.

sema
(math/tanh 0)      ; => 0.0
(math/tanh 1)      ; => 0.7615...

Exponential & Logarithmic

math/exp

Euler's number raised to a power (e^x).

sema
(math/exp 1)       ; => 2.71828...
(math/exp 0)       ; => 1.0

math/log10

Base-10 logarithm.

sema
(math/log10 100)   ; => 2.0
(math/log10 1000)  ; => 3.0

math/log2

Base-2 logarithm.

sema
(math/log2 8)      ; => 3.0
(math/log2 1024)   ; => 10.0

Integer Division

These operate on exact integers, are bignum-aware (they promote past i64 automatically), and raise on a zero divisor. Each has a slash-namespaced alias (math/quotient, math/remainder, math/gcd, math/lcm) that is the identical function.

quotient

Truncated integer division: n ÷ d rounded toward zero, so (quotient -7 2) is -3 (not floored to -4), per R7RS. Alias: math/quotient.

sema
(quotient 10 3)   ; => 3
(quotient -7 2)   ; => -3
(quotient 100000000000000000000 7)  ; => 14285714285714285714

remainder

Remainder of truncated division: the result takes the sign of the dividend n. Pairs with quotient so that (+ (* (quotient n d) d) (remainder n d)) reconstructs n. Contrast mod, whose sign follows the divisor. Alias: math/remainder.

sema
(remainder 10 3)  ; => 1
(remainder -7 2)  ; => -1
(remainder 7 -2)  ; => 1

gcd

Greatest common divisor — the largest non-negative integer dividing every argument. Variadic and sign-independent; (gcd) is 0. Alias: math/gcd.

sema
(gcd 12 8)     ; => 4
(gcd 15 10 25) ; => 5
(gcd -12 8)    ; => 4
(gcd)          ; => 0

lcm

Least common multiple — the smallest non-negative integer every argument divides. Variadic and sign-independent; 0 if any argument is 0; (lcm) is 1. Alias: math/lcm.

sema
(lcm 4 6)      ; => 12
(lcm 2 3 4)    ; => 12
(lcm 0 5)      ; => 0
(lcm)          ; => 1

Exactness & Rationals

Utilities for moving between exact and inexact forms and for working with exact rationals. See The Numeric Tower for the underlying model.

exact

Convert a number to its exact form. A finite float becomes the exact rational it actually represents (reduced, and normalized to an integer when the denominator is 1); already-exact numbers pass through. inexact->exact is the longer R7RS spelling.

sema
(exact 0.5)       ; => 1/2
(exact 2.0)       ; => 2
(exact 1/3)       ; => 1/3
(exact 3.14159)   ; => 3537115888337719/1125899906842624

The last result is exact but surprising: 3.14159 is not representable in binary, so exact returns the precise fraction the double stores. Use rationalize for a tidy approximation.

inexact

Convert a number to inexact (floating-point) form — an exact rational becomes its nearest f64, and each part of a complex becomes a float. exact->inexact is the longer R7RS spelling.

sema
(inexact 1/3)   ; => 0.3333333333333333
(inexact 42)    ; => 42.0
(inexact 3+4i)  ; => 3.0+4.0i

exact->inexact

R7RS spelling of inexact — identical behavior.

sema
(exact->inexact 1/3)   ; => 0.3333333333333333
(exact->inexact 42)    ; => 42.0

inexact->exact

R7RS spelling of exact — identical behavior.

sema
(inexact->exact 0.5)   ; => 1/2
(inexact->exact 2.0)   ; => 2
(inexact->exact 0.1)   ; => 3602879701896397/36028797018963968

0.1 shows the caveat: it has no finite binary expansion, so its exact value is that large power-of-two fraction, not 1/10.

numerator

Numerator of an exact rational, taken in lowest terms (the sign lives on the numerator). An integer n is n/1. A float or complex argument raises a type error — convert with exact first.

sema
(numerator 22/7)   ; => 22
(numerator -6/4)   ; => -3
(numerator 42)     ; => 42

denominator

Denominator of an exact rational, in lowest terms. An integer's denominator is 1.

sema
(denominator 22/7)   ; => 7
(denominator -6/4)   ; => 2
(denominator 42)     ; => 1

rationalize

Find the simplest rational within tol of x (smallest denominator in [x-|tol|, x+|tol|]), per R7RS. Exactness follows contagion: the result is exact only when both arguments are exact.

sema
(rationalize 1/3 1/1000)            ; => 1/3
(rationalize (exact 3.14159) 1/100) ; => 22/7
(rationalize 3.14159 1/100)         ; => 3.142857142857143
(rationalize 1/2 0.01)              ; => 0.5

The last two show contagion — an inexact x or an inexact tol gives an inexact (float) answer.

exact-integer-sqrt

Exact integer square root of a non-negative integer. Returns a two-element list (s r) with s = ⌊√n⌋ and s*s + r = n; exact even for bignums.

sema
(exact-integer-sqrt 17)   ; => (4 1)
(exact-integer-sqrt 100)  ; => (10 0)
(exact-integer-sqrt 15241578750190521) ; => (123456789 0)

Complex Numbers

A complex number a+bi has a real and an imaginary part. A complex whose imaginary part is exact zero collapses to a real, so real? is true for 3+0i. Polar conversions run through sin/cos/atan2 in floating point, so make-polar, magnitude, and angle are always inexact.

make-rectangular

Construct a complex from a real and an imaginary part. An exact-zero imaginary part collapses to the real; an inexact zero (0.0) stays complex.

sema
(make-rectangular 3 4)     ; => 3+4i
(make-rectangular 1/3 1/2) ; => 1/3+1/2i
(make-rectangular 2 0)     ; => 2
(make-rectangular 3 0.0)   ; => 3+0.0i

make-polar

Construct a complex from magnitude r and angle θ (radians): r·cos θ + r·sin θ·i. Always inexact.

sema
(make-polar 2 0)                ; => 2.0+0.0i
(make-polar 5 (math/atan2 3 4)) ; => 4.0+3.0i

real-part

Real part of a number (the number itself for a real). Preserves exactness.

sema
(real-part 3+4i)   ; => 3
(real-part 5i)     ; => 0
(real-part 2.5)    ; => 2.5

imag-part

Imaginary part of a number (exact 0 for any real). Preserves exactness.

sema
(imag-part 3+4i)   ; => 4
(imag-part 5i)     ; => 5
(imag-part 2.5)    ; => 0

magnitude

Magnitude (modulus, absolute value). For a complex a+bi this is √(a²+b²), computed in floating point (so it is inexact); for a real it is the absolute value and preserves exactness.

sema
(magnitude 3+4i)   ; => 5.0
(magnitude -5)     ; => 5
(magnitude 1/3)    ; => 1/3

angle

Angle (argument) of a complex in radians, in (-π, π]: atan2(b, a). Always inexact — a positive real gives 0.0, a negative real gives π.

sema
(angle 3+4i)   ; => 0.9272952180016122
(angle 5)      ; => 0.0
(angle -5)     ; => 3.141592653589793

Number ↔ String

number->string

Render any number in the tower as a string. An optional radix of 2, 8, 10, or 16 selects the output base — but a non-decimal radix accepts only exact integers.

sema
(number->string 42)     ; => "42"
(number->string 1/3)    ; => "1/3"
(number->string 3+4i)   ; => "3+4i"
(number->string 255 16) ; => "ff"
(number->string 5 2)    ; => "101"

string->number

Parse a string as a number, returning #f (never an error) on invalid input. The default radix 10 accepts the whole tower (integers, rationals, floats, complex); a radix of 2, 8, or 16 parses an integer in that base.

sema
(string->number "42")    ; => 42
(string->number "1/3")   ; => 1/3
(string->number "3+4i")  ; => 3+4i
(string->number "ff" 16) ; => 255
(string->number "nope")  ; => #f

Random Numbers

math/random

Return a random float between 0.0 (inclusive) and 1.0 (exclusive).

sema
(math/random)      ; => 0.7291... (varies)

math/random-int

Return a random integer in a range (inclusive on both ends).

sema
(math/random-int 1 100)  ; => 42 (varies)
(math/random-int 0 9)    ; => 7 (varies)

Interpolation & Clamping

math/clamp

Clamp a value to a range.

sema
(math/clamp 15 0 10)   ; => 10
(math/clamp -5 0 10)   ; => 0
(math/clamp 5 0 10)    ; => 5

math/sign

Return the sign of a number: -1, 0, or 1.

sema
(math/sign -5)     ; => -1
(math/sign 0)      ; => 0
(math/sign 42)     ; => 1

math/lerp

Linear interpolation between two values. (math/lerp a b t) returns a + (b - a) * t.

sema
(math/lerp 0 100 0.5)   ; => 50.0
(math/lerp 0 100 0.25)  ; => 25.0
(math/lerp 10 20 0.0)   ; => 10.0

math/map-range

Map a value from one range to another. (math/map-range value in-min in-max out-min out-max).

sema
(math/map-range 5 0 10 0 100)    ; => 50.0
(math/map-range 0.5 0 1 0 255)   ; => 127.5

Angle Conversion

math/degrees->radians

Convert degrees to radians.

sema
(math/degrees->radians 180)   ; => 3.14159...
(math/degrees->radians 90)    ; => 1.5707...

math/radians->degrees

Convert radians to degrees.

sema
(math/radians->degrees pi)    ; => 180.0
(math/radians->degrees 1)     ; => 57.295...

Numeric Predicates

even?

Test if an integer is even.

sema
(even? 4)      ; => #t
(even? 3)      ; => #f

odd?

Test if an integer is odd.

sema
(odd? 3)       ; => #t
(odd? 4)       ; => #f

positive?

Test if a number is positive.

sema
(positive? 1)  ; => #t
(positive? -1) ; => #f
(positive? 0)  ; => #f

negative?

Test if a number is negative.

sema
(negative? -1) ; => #t
(negative? 1)  ; => #f

zero?

Test if a number is zero.

sema
(zero? 0)      ; => #t
(zero? 1)      ; => #f

math/nan?

Test if a value is NaN (not a number).

sema
(math/nan? math/nan)       ; => #t
(math/nan? 42)             ; => #f

math/infinite?

Test if a value is infinite.

sema
(math/infinite? math/infinity)  ; => #t
(math/infinite? 42)             ; => #f

Constants

pi

The mathematical constant π (3.14159...).

sema
pi             ; => 3.141592653589793

e

Euler's number (2.71828...).

sema
e              ; => 2.718281828459045

math/infinity

Positive infinity.

sema
math/infinity  ; => Inf

math/nan

Not a number.

sema
math/nan       ; => NaN

Scheme Aliases

modulo

Alias for mod.

sema
(modulo 10 3)  ; => 1

expt

Alias for pow (Scheme name for exponentiation). With exact integer arguments the result is exact and bignum-aware — no overflow — and a negative exponent yields an exact rational.

sema
(expt 2 10)    ; => 1024
(expt 2 100)   ; => 1267650600228229401496703205376
(expt 2 -1)    ; => 1/2

ceiling

Alias for ceil (exactness-preserving, so a float rounds to a float).

sema
(ceiling 3.2)  ; => 4.0

truncate

Round toward zero (drop the fractional part). Exactness-preserving: a float truncates to a float, an exact rational to an exact integer.

sema
(truncate 3.7)  ; => 3.0
(truncate -3.7) ; => -3.0
(truncate 7/2)  ; => 3

Bitwise Operations

bit/and

Bitwise AND.

sema
(bit/and 5 3)      ; => 1
(bit/and 15 9)     ; => 9

bit/or

Bitwise OR.

sema
(bit/or 5 3)       ; => 7
(bit/or 8 4)       ; => 12

bit/xor

Bitwise XOR.

sema
(bit/xor 5 3)      ; => 6

bit/not

Bitwise NOT (complement).

sema
(bit/not 5)        ; => -6

bit/shift-left

Left bit shift.

sema
(bit/shift-left 1 4)   ; => 16
(bit/shift-left 3 2)   ; => 12

bit/shift-right

Right bit shift.

sema
(bit/shift-right 16 2) ; => 4
(bit/shift-right 8 1)  ; => 4