A Simple C Trick for Readable Vector Math
One of C’s most commonly acknowledged shortcomings is the lack of operator overloading. If you’re writing code that deals with math or physics, and is dealing with vectors, matrices and quaternions, you’d have to wrap everything in function calls and that can get annoying very fast.
A few years ago, I stumbled across a trick from John Carmack in the original Quake source code. It’s super simple, and makes writing vector math in C a hell of a lot easier, and I haven’t seen anyone talk about it.
Let’s say, you want to compute E in this imaginary formula where A, B, C, D, E are all 3D vectors:
E = (A * B) + (C - D) * sqrt(pi)With using the traditional approach of using helper functions, the code would look something like this:
E = mulV3F(addV3(mulV3(A, B), subV3(C, D)), sqrtf(pi));This is hard to read, and can be easy to mess up, and the nesting obscures the actual math.
Instead, by using a for loop, the same code can be expressed in:
typedef float vec_t;
typedef vec_t vec3_t[3];
vec3_t A, B, C, D, E;
for (int i = 0; i < 3; i++) {
E[i] = (A[i] * B[i]) + (C[i] - D[i]) * sqrtf(pi);
}Another example:
float dt = ...
float mass = ...
vec3_t force = ...
vec3_t velocity;
vec3_t position;
for (int i = 0; i < 3; i++) {
velocity[i] += (force[i] / mass) * dt;
position[i] += velocity[i] * dt;
}Compared to:
velocity = addV3(velocity, mulV3F(divV3F(force, mass), dt));
position = addV3(position, mulV3F(velocity, dt));You can also take this a step further by wrapping the vec3 array in a union, giving you both named access and array-style iteration.
typedef union vec3_t {
struct { float x, y, z; };
struct { float r, g, b; };
float E[3];
} vec3_t;
vec3_t wishdir, acceleration, position, velocity;
wishdir.x = ...
wishdir.z = ...
for (int i = 0; i < 3; i++) {
acceleration.E[i] += wishdir.E[i] * speed;
velocity.E[i] += acceleration.E[i] * dt;
position.E[i] += velocity.E[i] * dt;
}I mix this technique with traditional helper functions. For simple operations like adding or multiplying two vectors, I prefer the functions, but for more complex operations like physics integration, interpolation or compound expressions, I prefer the loop approach.
It’s a simple trick, but I found it to be very useful!


