The Math Hiding in Everyday Code
A few examples of how ordinary programming ideas, from modulo to floating-point comparisons, are really small pieces of math.
Maruf Hossain4 min read
A lot of programming feels practical on the surface: write a condition, store a value, loop through data, compare two numbers.
But underneath those small actions, there is usually some math. Not always advanced math. Often it is the kind of math that quietly shapes how the code behaves.
Here are a few examples I keep running into.
Modulo as Wrapping
The modulo operator is easy to treat as "the remainder thing." But it is also a way to wrap values into a fixed range.
#include <iostream>
using namespace std;
int bucketFor(int studentId) {
return studentId % 10;
}
int main() {
int id = 123456;
cout << "Bucket index: " << bucketFor(id) << endl;
return 0;
}Here, % 10 maps a student ID into one of ten buckets. The same idea shows up in hash tables, circular buffers, clocks, scheduling, and simple game loops.
Modulo is not just arithmetic. It is a way to keep values inside boundaries.
There is one edge case worth remembering in C++: the remainder keeps the sign of the left operand. If negative inputs are possible, value % size can be negative. A normalization such as ((value % size) + size) % size keeps the result in the range from 0 to size - 1.
Boolean Logic in Conditions
Every if statement is a small logic expression.
bool isStudent = true;
bool hasPaidFees = false;
if (isStudent && hasPaidFees) {
cout << "Access granted." << endl;
} else {
cout << "Access denied." << endl;
}&&, ||, and ! look like programming syntax, but they are logical operations. The code is asking whether two conditions are both true, whether at least one is true, or whether something is false.
That matters because messy conditionals usually mean messy thinking. When the logic is unclear, the bug is often hiding in the assumptions.
Floating-Point Comparisons
Decimals can be surprising in code.
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
bool nearlyEqual(double a, double b,
double relativeTolerance = 1e-9,
double absoluteTolerance = 1e-12) {
double difference = abs(a - b);
double scale = max(abs(a), abs(b));
return difference <= max(absoluteTolerance, relativeTolerance * scale);
}
int main() {
double a= 0.1 * 3;
double b= 0.3;
if (nearlyEqual(a, b)) {
cout << "Equal" << endl;
} else {
cout << "Not equal" << endl;
}
return 0;
}Mathematically, 0.1 * 3 should equal 0.3. In binary floating-point, some decimal values cannot be represented exactly, so tiny rounding errors appear.
That is why comparing computed floating-point values with == can be risky. The safer question is often: are these values close enough for the scale of this problem?
A single fixed epsilon works for a narrow range of values. Combining a small absolute tolerance with a relative tolerance makes the comparison useful near zero and at larger magnitudes. The correct tolerances still come from the application: measurements, money, graphics, and scientific simulations do not share one universal definition of "close enough."
Graphs as Relationships
Graph theory appears whenever code models relationships.
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
using namespace std;
class SocialNetwork {
private:
unordered_map<string, vector<string>> friends;
public:
void addFriendship(const string& person1, const string& person2) {
friends[person1].push_back(person2);
friends[person2].push_back(person1);
}
bool areFriends(const string& person1, const string& person2) {
if (friends.find(person1) == friends.end()) return false;
for (const string& friendName : friends[person1]) {
if (friendName == person2) return true;
}
return false;
}
};People are vertices. Friendships are edges. Once you see that, a lot of features become graph problems: mutual friends, recommendations, shortest paths, communities, and influence.
The useful part is not memorizing graph terminology. It is recognizing when your data is really a network.
The representation also encodes a tradeoff. A vector keeps a compact list of neighbours, but checking whether two people are connected takes time proportional to one person's number of friends. If membership checks dominate the workload, an unordered_set can make that lookup faster at the cost of more memory. The math identifies the graph; the product's access patterns help choose its representation.
The Pattern
The math in everyday code is usually not loud. It shows up as:
- wrapping values into a range
- combining conditions
- comparing approximate numbers
- modeling relationships
- updating state over time
Learning the math does not make code magically better, but it does make the behavior less mysterious. You stop treating operators and data structures as tricks and start seeing the ideas behind them.
That is when programming gets more interesting.
— Maruf