Everything in an FFT — all 2,304 operations in a 512-point transform — is this one tiny
thing repeated. Learn it here and you've learned the FFT's entire arithmetic. It even
looks nice on paper.
What You'll Build
The butterfly: a two-in, two-out operation that shares one complex multiplication between both
outputs. Then you'll see how butterflies arrange into stages.
Learning Objectives
Perform a complex multiplication with four real multiplies
Execute a butterfly by hand and in code
Explain why both outputs reuse the same product
Describe how the pairing distance doubles each stage
Count the butterflies in an N-point FFT
Concepts Introduced
ID
Concept
384
Butterfly Structure
385
Complex Multiplication
386
Four Multiply Form
387
Butterfly Pair
388
Stage Span
389
Data Flow Graph
390
Butterfly Count
391
Stage Loop
392
Cross Add And Subtract
Background
The shape
12345678
a ──────────┬─────────► a + W·b
╲ ╱
╲ ╱
╲ ╱
╳
╱ ╲
╱ ╲
b ──[× W]────┴───────► a − W·b
Two complex inputs, two complex outputs, crossing over in the middle. Hence "butterfly."
The arithmetic
Step 1 — multiply b by the twiddle. Complex multiplication needs four real multiplies:
12
tr=wr*br-wi*bi# real partti=wr*bi+wi*br# imaginary part
Step 2 — cross add and subtract. No more multiplying:
12
out1=a+tout2=a-t
Total: 4 real multiplies, 6 real adds, two complex outputs.
The saving is the sharing
Notice that W·b is computed once and used for both outputs. The direct DFT computes
those two bins separately, redoing that multiply. Multiply that waste across every bin
and every stage and you have the entire 500× gap from Lab 16.
Stages
123
Stage 1: pairs are 1 apart (0,1) (2,3) (4,5) (6,7)
Stage 2: pairs are 2 apart (0,2) (1,3) (4,6) (5,7)
Stage 3: pairs are 4 apart (0,4) (1,5) (2,6) (3,7)
Stage 1 pairs neighbours. Each stage the reach doubles until the last stage spans half the
array. For N = 512 that's nine stages — the log₂(N) from Lab 17, made concrete.
Every stage does exactly N/2 butterflies, so the total is (N/2)·log₂(N).
# Lab 19: The Butterfly## We have the pieces now:# Lab 17 -- split into evens and odds, recombine with a twiddle factor# Lab 18 -- reorder once by bit reversal, then work in place## The recombination step is where all the arithmetic happens, and it has a# name. Draw it and it looks like a butterfly's wings:## a ------+-------> a + W*b# \ /# \ /# \ /# X# / \# / \# / \# b --[W]--+-------> a - W*b## Two inputs, two outputs, ONE complex multiplication shared between them.# That sharing is the whole reason the FFT is fast.importconfigimportmath# =========================================================================# PART 1 -- one butterfly, step by step# =========================================================================print("=== PART 1: a single butterfly ===")print()# Two complex numbers, a and bar,ai=3.0,1.0br,bi=2.0,-1.0# A twiddle factor: a rotation of -45 degreesangle=-math.pi/4wr,wi=math.cos(angle),math.sin(angle)print("input a = %+.3f%+.3fi"%(ar,ai))print("input b = %+.3f%+.3fi"%(br,bi))print("twiddle W= %+.3f%+.3fi (rotate by %.0f degrees)"%(wr,wi,math.degrees(angle)))print()# Step 1: multiply b by the twiddle factor.# Complex multiply: (wr + wi*i)(br + bi*i)# = wr*br - wi*bi + (wr*bi + wi*br)itr=wr*br-wi*biti=wr*bi+wi*brprint("Step 1 -- W * b (four real multiplies):")print(" real = wr*br - wi*bi = %+.3f*%+.3f - %+.3f*%+.3f = %+.4f"%(wr,br,wi,bi,tr))print(" imag = wr*bi + wi*br = %+.3f*%+.3f + %+.3f*%+.3f = %+.4f"%(wr,bi,wi,br,ti))print()# Step 2: add and subtract. Both outputs reuse the SAME product.out1r,out1i=ar+tr,ai+tiout2r,out2i=ar-tr,ai-tiprint("Step 2 -- combine (no more multiplying):")print(" top = a + W*b = %+.4f%+.4fi"%(out1r,out1i))print(" bottom = a - W*b = %+.4f%+.4fi"%(out2r,out2i))print()print("Total cost: 4 real multiplies and 6 real adds, for TWO outputs.")# =========================================================================# PART 2 -- the butterfly as a function# =========================================================================print()print("=== PART 2: as reusable code ===")defbutterfly(re,im,i1,i2,wr,wi):"""One butterfly, performed in place on two positions of an array."""# W * x[i2]tr=wr*re[i2]-wi*im[i2]ti=wr*im[i2]+wi*re[i2]# cross add and subtractar,ai=re[i1],im[i1]re[i1]=ar+trim[i1]=ai+tire[i2]=ar-trim[i2]=ai-tire=[3.0,2.0]im=[1.0,-1.0]butterfly(re,im,0,1,wr,wi)print("same numbers through the function:")print(" x[0] = %+.4f%+.4fi"%(re[0],im[0]))print(" x[1] = %+.4f%+.4fi"%(re[1],im[1]))# =========================================================================# PART 3 -- how butterflies are arranged into stages# =========================================================================print()print("=== PART 3: stages ===")print()N=8stages=int(math.log(N,2)+0.5)print("For N=%d there are %d stages, each with %d butterflies."%(N,stages,N//2))print("Total: %d butterflies. Compare to %d operations for the direct DFT."%(stages*N//2,N*N))print()half=1stage=1whilehalf<N:span=half*2print("Stage %d: pairs are %d apart, blocks of %d"%(stage,half,span))pairs=[]forkinrange(0,N,span):forjinrange(half):pairs.append((k+j,k+j+half))print(" "," ".join("(%d,%d)"%pforpinpairs))half*=2stage+=1print()print("Stage 1 pairs neighbours. Each stage the reach doubles, until the")print("last stage pairs elements half the array apart. Nine stages covers")print("512 samples -- that is the log2(N) in N*log2(N).")# =========================================================================# PART 4 -- how many butterflies, really# =========================================================================print()print("=== PART 4: counting the work ===")print("%8s%8s%14s%16s%10s"%("N","stages","butterflies","direct DFT ops","ratio"))fornin(8,64,256,512,1024):st=int(math.log(n,2)+0.5)bf=st*n//2print("%8d%8d%14d%16d%9.0fx"%(n,st,bf,n*n,(n*n)/bf))print()print("512 samples: 2304 butterflies instead of 262144 operations.")print("Each butterfly is 4 multiplies -- so about 9216 multiplications")print("where the DFT needed over half a million.")
Part 1 shows every intermediate value for a single butterfly with a = 3+1i, b = 2−1i, and a
45° twiddle. Follow the arithmetic with a calculator once — it makes the rest concrete.
Eight lines. This is the FFT's entire arithmetic. Everything else is loops deciding which
indices and which twiddle.
Save a before you overwrite it
Notice ar, ai = re[i1], im[i1] happens before any assignment. Write re[i1] first
and the old value is gone when you need it for re[i2]. It's a two-character bug that
produces a spectrum which looks plausible and is wrong.
Step 3 — Watch the stages spread
Part 3 prints the pairing for every stage of an 8-point FFT. Trace one element — say index 1 —
through all three stages and see who it partners with each time.
Step 4 — Count the work
12
N stages butterflies direct DFT ops ratio
512 9 2304 262144 114x
2,304 butterflies for a 512-point FFT. At 4 multiplies each that's about 9,216
multiplications, where the DFT needed over half a million.
Step 5 — Predict, then measure
Prediction: how many butterflies in a 1,024-point FFT? How many stages?
Work it out before checking the table.
Expected Output
12345678
Step 1 -- W * b (four real multiplies):
real = wr*br - wi*bi = +0.707*+2.000 - -0.707*-1.000 = +0.7071
imag = wr*bi + wi*br = +0.707*-1.000 + -0.707*+2.000 = -2.1213
Stage 1: pairs are 1 apart, blocks of 2
(0,1) (2,3) (4,5) (6,7)
512 9 2304 262144 114x
Troubleshooting
Symptom
Likely cause
Fix
Outputs wrong but plausible
Overwrote a before using it
Save ar, ai first
Complex multiply wrong
Sign error
tr = wr·br − wi·bi; the minus is on the real part
Pairs overlap between blocks
Block stride wrong
Blocks advance by half*2
Only half the array changes
Inner loop bound wrong
It runs half times, not n
Challenges
Three multiplies. There's a known trick computing a complex product with 3 multiplies and
5 adds instead of 4 and 2. Look it up, implement it, and decide whether it's worth it here.
Draw the graph. Sketch the full 8-point data-flow diagram, all three stages. You'll see
why it's called a butterfly network.
Trace an element. Follow index 3 through all three stages of an 8-point FFT. Which
partners does it meet, and which twiddle applies each time?
Check Your Understanding
How many real multiplies does one butterfly need, and for how many outputs?
Why must you save a before writing the outputs?
In stage 3 of a 16-point FFT, how far apart are the paired elements?
How many butterflies are in a 256-point FFT?
In one sentence, where does the FFT's saving actually come from?
You have every piece now
Split, reorder, look up twiddles, butterfly. Next lab you assemble all four into a
working FFT — and find out just how much faster it really is.