Dividing Without Dividing
This is another problem from Leetcode. When I first see the problem, I tried solving it by transforming both numbers to binary equivalents. I didn’t know how to apply division on a binary number, and that fascinated me as well. However, that also included some division which was not “dividing without dividing”. Instead, I went with a subtraction approach.
The Problem
Given two integers, divide them and return the truncated quotient — without using multiplication, division, or the modulo operator.
The Naive Subtraction Approach: Full Loop
If I subtract the divisor repeatedly and count how many times it fits:
while ($dividend >= $divisor) {
$dividend -= $divisor;
$quotient++;
}
This works, but for something like 2147483647 / 1 (in a 32-bit system) it loops over 2 billion times. That’s O(n) — far too slow.
Faster Subtraction Approach: Exponential Doubling
Instead of subtracting one divisor at a time, I can subtract the largest possible chunk at each step. I find that chunk by doubling the divisor using left bit shifts (<<) until it no longer fits.
3 << 1 = 6 (3 × 2)
3 << 2 = 12 (3 × 4)
3 << 3 = 24 (3 × 8)
Each left shift multiplies the number by 2 because bits are binary (0 and 1).
The Code
function divide($dividend, $divisor) {
if ($dividend == 0) {
return 0;
}
// Whether the result will be negative or not
$negative = false;
if ($dividend < 0 && $divisor > 0) {
$negative = true;
}
if ($dividend > 0 && $divisor < 0) {
$negative = true;
}
$dividend = abs($dividend);
$divisor = abs($divisor);
// If dividend is less than divisor, quotient will always be 0
if ($dividend < $divisor) {
return 0;
}
$quotient = 0;
$nextDividend = $dividend;
while ($nextDividend >= $divisor) {
$multiplier = 1;
$nextDivisor = $divisor;
while ($nextDividend >= ($nextDivisor << 1)) {
$multiplier <<= 1;
$nextDivisor <<= 1;
}
$quotient += $multiplier;
$nextDividend = $nextDividend - $nextDivisor;
}
if ($negative) {
$quotient = -$quotient;
}
return $quotient;
}
Visual Walkthrough: 38 ÷ 3
flowchart TD
A["Start\ndividend=38, divisor=3"] --> B["Both positive → negative=false\nquotient=0, nextDividend=38"]
B --> D{"nextDividend 38\n≥ divisor 3?"}
D -->|Yes - Pass 1| E["multiplier=1, nextDivisor=3"]
E --> E1{"38 ≥ 3<<1=6?"} -->|Yes| E2["multiplier=2, nextDivisor=6"]
E2 --> E3{"38 ≥ 6<<1=12?"} -->|Yes| E4["multiplier=4, nextDivisor=12"]
E4 --> E5{"38 ≥ 12<<1=24?"} -->|Yes| E6["multiplier=8, nextDivisor=24"]
E6 --> E7{"38 ≥ 24<<1=48?"} -->|No - STOP| E8["quotient = 0+8 = 8\nnextDividend = 38-24 = 14"]
E8 --> F{"nextDividend 14\n≥ divisor 3?"}
F -->|Yes - Pass 2| G["multiplier=1, nextDivisor=3"]
G --> G1{"14 ≥ 3<<1=6?"} -->|Yes| G2["multiplier=2, nextDivisor=6"]
G2 --> G3{"14 ≥ 6<<1=12?"} -->|Yes| G4["multiplier=4, nextDivisor=12"]
G4 --> G5{"14 ≥ 12<<1=24?"} -->|No - STOP| G6["quotient = 8+4 = 12\nnextDividend = 14-12 = 2"]
G6 --> H{"nextDividend 2\n≥ divisor 3?"}
H -->|No - Exit| I["negative=false\nReturn 12 ✓"]
Step-by-Step State Table
| Pass | nextDividend | Largest chunk found | multiplier | quotient | Remainder |
|---|---|---|---|---|---|
| 1 | 38 | 24 (3 × 8) | 8 | 8 | 14 |
| 2 | 14 | 12 (3 × 4) | 4 | 12 | 2 |
| 3 | 2 | 2 < 3, stop | — | 12 | 2 |
The remainder 2 is discarded, giving me the truncated result of 12.
Time Complexity
Instead of subtracting 3 from 38 twelve times, exponential doubling did it in 2 passes by taking the biggest possible bite each time. The number of iterations shrinks exponentially — making this O(log² n) rather than O(n).
Sign Handling
The sign is resolved upfront and applied at the end, so the core loop always works with positive numbers:
(+) ÷ (+) = +
(−) ÷ (−) = +
(+) ÷ (−) = −
(−) ÷ (+) = −
If either (but not both) inputs are negative, $negative is set to true and the quotient is flipped before returning.