Skip to content

Arithmetic and Comparisons

WebAssembly provides a rich set of arithmetic and comparison instructions. Each instruction is typed — you always know whether you are working with integers or floats, and for integers, whether the operation is signed or unsigned.

The four basic operations for i32 are i32.add, i32.sub, i32.mul, and division. All pop two operands and push one result:

(module
(func (export "add") (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add)
(func (export "sub") (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.sub)
(func (export "mul") (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.mul))

The same instructions exist for i64: i64.add, i64.sub, i64.mul.

Division is where integer types reveal their complexity. An i32 is just 32 bits — the bits themselves carry no sign information. The instruction decides whether to interpret those bits as a two’s-complement signed integer or as an unsigned integer:

(module
;; signed: -7 / 2 = -3 (rounds toward zero)
(func (export "divSigned") (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.div_s)
;; unsigned: the same bit pattern as -7 (0xFFFFFFF9) / 2 = 2147483644
(func (export "divUnsigned") (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.div_u))

Similarly i32.rem_s and i32.rem_u give signed and unsigned remainder.

Float instructions mirror integer ones, but the type prefix changes:

(module
(func (export "fadd") (param $a f64) (param $b f64) (result f64)
local.get $a
local.get $b
f64.add))

f64.sub, f64.mul, f64.div, f64.sqrt, f64.floor, f64.ceil, f64.min, and f64.max are all available. The same set exists for f32.

Comparisons pop two values and push an i32 result: 1 for true, 0 for false. There is no boolean type — WebAssembly uses i32 as a boolean whenever it needs one.

(module
;; returns 1 if $a equals $b, else 0
(func (export "eq") (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.eq)
;; signed less-than: returns 1 if $a < $b (signed), else 0
(func (export "ltSigned") (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.lt_s))

Comparison variants follow the same signed/unsigned pattern: i32.lt_s vs i32.lt_u, i32.gt_s vs i32.gt_u, i32.le_s, i32.ge_u, and so on.

The example below compiles a module with subtraction, signed division, and a signed less-than comparison.

WebAssembly
What does `i32.lt_s` return when the comparison is true?
What is the difference between `i32.div_s` and `i32.div_u`?
Which instruction multiplies two i32 values?
Wasm comparisons like `i32.eq` push what type onto the stack?