This page is a placeholder. All examples on this page are currently AI-generated and are not correct. This documentation will be completed in the future with accurate, tested examples.
Overview
Opcode: 0x04
Introduced: Frontier (EVM genesis)
DIV performs unsigned integer division on two 256-bit values. Unlike most programming languages, division by zero returns 0 instead of throwing an exception, preventing denial-of-service attacks.
This operation is essential for ratio calculations, scaling, and implementing fractional arithmetic in smart contracts.
Specification
Stack Input:
a (top - dividend)
b (divisor)
Stack Output:
a / b (if b ≠ 0)
0 (if b = 0)
Gas Cost: 5 (GasFastStep)
Operation:
result = (b == 0) ? 0 : (a / b)
Behavior
DIV pops two values from the stack, performs integer division (truncating toward zero), and pushes the quotient:
- If
b ≠ 0: Result is floor(a / b) (truncated)
- If
b = 0: Result is 0 (no exception)
The result is always the integer quotient with remainder discarded. Use MOD to get the remainder.
Examples
Basic Division
import { div } from '@tevm/voltaire/evm/arithmetic';
import { createFrame } from '@tevm/voltaire/evm/Frame';
// 10 / 2 = 5
const frame = createFrame({ stack: [10n, 2n] });
const err = div(frame);
console.log(frame.stack); // [5n]
console.log(frame.gasRemaining); // Original - 5
Division with Remainder
// 10 / 3 = 3 (remainder 1 discarded)
const frame = createFrame({ stack: [10n, 3n] });
const err = div(frame);
console.log(frame.stack); // [3n]
Division by Zero
// Division by zero returns 0 (no exception)
const frame = createFrame({ stack: [42n, 0n] });
const err = div(frame);
console.log(frame.stack); // [0n]
console.log(err); // null (no error!)
Division by One
// Identity: n / 1 = n
const frame = createFrame({ stack: [42n, 1n] });
const err = div(frame);
console.log(frame.stack); // [42n]
Large Division
// Large number division
const MAX = (1n << 256n) - 1n;
const frame = createFrame({ stack: [MAX, 2n] });
const err = div(frame);
// Result: floor(MAX / 2) = 2^255 - 1
console.log(frame.stack); // [(MAX - 1n) / 2n]
Gas Cost
Cost: 5 gas (GasFastStep)
DIV costs the same as MUL and MOD, more than ADD/SUB due to increased complexity:
Comparison:
- ADD/SUB: 3 gas
- MUL/DIV/MOD/SDIV/SMOD/SIGNEXTEND: 5 gas
- ADDMOD/MULMOD: 8 gas
- EXP: 10 + 50 per byte
Division is ~67% more expensive than addition but significantly cheaper than repeated subtraction.
Edge Cases
Zero Division
// 0 / 0 = 0 (special case)
const frame = createFrame({ stack: [0n, 0n] });
div(frame);
console.log(frame.stack); // [0n]
Self-Division
// n / n = 1 (except when n = 0)
const frame = createFrame({ stack: [42n, 42n] });
div(frame);
console.log(frame.stack); // [1n]
Division Truncation
// Truncates toward zero
const cases = [
[10n, 3n], // 10/3 = 3
[100n, 9n], // 100/9 = 11
[7n, 2n], // 7/2 = 3
];
for (const [a, b] of cases) {
const frame = createFrame({ stack: [a, b] });
div(frame);
console.log(frame.stack[0]); // Truncated quotients
}
Maximum Value Division
// MAX / MAX = 1
const MAX = (1n << 256n) - 1n;
const frame = createFrame({ stack: [MAX, MAX] });
div(frame);
console.log(frame.stack); // [1n]
Common Usage
Ratio Calculations
// Calculate percentage
function calculatePercentage(uint256 amount, uint256 percent)
pure returns (uint256) {
return (amount * percent) / 100;
}
// Calculate share from total
function calculateShare(uint256 userAmount, uint256 totalAmount, uint256 reward)
pure returns (uint256) {
return (userAmount * reward) / totalAmount;
}
Fixed-Point Division
// 18 decimal fixed-point division
uint256 constant WAD = 1e18;
function wdiv(uint256 x, uint256 y) pure returns (uint256) {
return (x * WAD) / y; // Scale first to preserve precision
}
// Example: 1.5 / 2.5 = 0.6
// (1.5e18 * 1e18) / 2.5e18 = 0.6e18
Average Calculation
// Simple average (beware overflow)
function average(uint256 a, uint256 b) pure returns (uint256) {
return (a + b) / 2;
}
// Safe average avoiding overflow
function safeAverage(uint256 a, uint256 b) pure returns (uint256) {
return (a / 2) + (b / 2) + (a % 2 + b % 2) / 2;
}
Scaling and Conversion
// Convert from higher to lower decimals
function scaleDown(uint256 amount, uint8 fromDecimals, uint8 toDecimals)
pure returns (uint256) {
require(fromDecimals >= toDecimals, "invalid decimals");
return amount / (10 ** (fromDecimals - toDecimals));
}
// Convert token amounts
function convertToUSDC(uint256 tokenAmount, uint256 price)
pure returns (uint256) {
// Assuming price is in USDC per token (6 decimals)
return (tokenAmount * price) / 1e18;
}
Implementation
/**
* DIV opcode (0x04) - Integer division (division by zero returns 0)
*/
export function div(frame: FrameType): EvmError | null {
// Consume gas (GasFastStep = 5)
frame.gasRemaining -= 5n;
if (frame.gasRemaining < 0n) {
frame.gasRemaining = 0n;
return { type: "OutOfGas" };
}
// Pop operands
if (frame.stack.length < 2) return { type: "StackUnderflow" };
const a = frame.stack.pop(); // dividend
const b = frame.stack.pop(); // divisor
// Division by zero returns 0 (no exception)
const result = b === 0n ? 0n : a / b;
// Push result
if (frame.stack.length >= 1024) return { type: "StackOverflow" };
frame.stack.push(result);
// Increment PC
frame.pc += 1;
return null;
}
Testing
Test Coverage
import { describe, it, expect } from 'vitest';
import { div } from './0x04_DIV.js';
describe('DIV (0x04)', () => {
it('divides two numbers', () => {
const frame = createFrame([10n, 2n]);
expect(div(frame)).toBeNull();
expect(frame.stack).toEqual([5n]);
});
it('truncates remainder', () => {
const frame = createFrame([10n, 3n]);
expect(div(frame)).toBeNull();
expect(frame.stack).toEqual([3n]);
});
it('handles division by zero', () => {
const frame = createFrame([42n, 0n]);
expect(div(frame)).toBeNull();
expect(frame.stack).toEqual([0n]);
});
it('handles zero divided by zero', () => {
const frame = createFrame([0n, 0n]);
expect(div(frame)).toBeNull();
expect(frame.stack).toEqual([0n]);
});
it('handles division by one', () => {
const frame = createFrame([42n, 1n]);
expect(div(frame)).toBeNull();
expect(frame.stack).toEqual([42n]);
});
it('handles self-division', () => {
const frame = createFrame([42n, 42n]);
expect(div(frame)).toBeNull();
expect(frame.stack).toEqual([1n]);
});
it('consumes correct gas (5)', () => {
const frame = createFrame([10n, 2n], 100n);
expect(div(frame)).toBeNull();
expect(frame.gasRemaining).toBe(95n);
});
});
Security
Division by Zero
Why DIV returns 0 instead of reverting:
// If DIV reverted on zero:
function maliciousRatio(uint256 numerator, uint256 denominator)
pure returns (uint256) {
return numerator / denominator;
}
// Attacker calls with denominator = 0
// Contract execution would halt
// This could DOS critical functionality
The EVM solution:
// Division by zero returns 0 (no revert)
// Contracts MUST explicitly check divisor
function safeRatio(uint256 numerator, uint256 denominator)
pure returns (uint256) {
require(denominator != 0, "division by zero");
return numerator / denominator;
}
Precision Loss
Problem: Integer division loses precision
// WRONG: Loses precision
function calculateFee(uint256 amount) pure returns (uint256) {
return (amount / 100) * 3; // 3% fee
}
// Example: amount = 55
// (55 / 100) * 3 = 0 * 3 = 0 (should be 1.65 ≈ 1)
Solution: Multiply first
// RIGHT: Preserve precision
function calculateFee(uint256 amount) pure returns (uint256) {
return (amount * 3) / 100; // Multiply first
}
// Example: amount = 55
// (55 * 3) / 100 = 165 / 100 = 1
Rounding Direction
// DIV always rounds DOWN (toward zero)
// For ceiling division:
function divCeil(uint256 a, uint256 b) pure returns (uint256) {
require(b > 0, "division by zero");
return (a + b - 1) / b;
}
// Examples:
// divCeil(10, 3) = (10 + 3 - 1) / 3 = 12 / 3 = 4
// divCeil(9, 3) = (9 + 3 - 1) / 3 = 11 / 3 = 3
Safe Fixed-Point Math
// Using PRBMath or similar library
import {UD60x18, ud} from "@prb/math/UD60x18.sol";
function safeDivision(uint256 x, uint256 y) pure returns (uint256) {
// Handles precision and overflow safely
UD60x18 result = ud(x).div(ud(y));
return result.unwrap();
}
Overflow in Multi-Step Calculations
// WRONG: Can overflow intermediate result
function mulDiv(uint256 x, uint256 y, uint256 denominator)
pure returns (uint256) {
return (x * y) / denominator; // x * y can overflow!
}
// RIGHT: Use assembly for 512-bit intermediate
function mulDiv(uint256 x, uint256 y, uint256 denominator)
pure returns (uint256 z) {
assembly {
// Full 512-bit multiplication
let mm := mulmod(x, y, not(0))
z := div(mul(x, y), denominator)
// Check for overflow
if iszero(and(
gt(denominator, 0),
or(iszero(mm), eq(div(mm, x), y))
)) { revert(0, 0) }
}
}
Benchmarks
DIV performance characteristics:
Relative execution time:
- ADD: 1.0x
- MUL: 1.2x
- DIV: 2.5x
- MOD: 2.5x
Gas efficiency:
- 5 gas per 256-bit division
- ~200,000 divisions per million gas
- Much faster than repeated subtraction (which would be ~3n gas for n subtractions)
Optimization tip:
// Division by constant powers of 2: use shift
uint256 result = x / 2; // 5 gas (DIV)
uint256 result = x >> 1; // 3 gas (SHR) - 40% cheaper!
uint256 result = x / 256; // 5 gas (DIV)
uint256 result = x >> 8; // 3 gas (SHR)
References