0.1f vs 0: Why C++ Code Slows Down 10x and How to Fix It

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: The Bug That Cost 10x Performance

\n\n

Imagine this: you're writing high-performance C++ code. Everything is flying, tests pass, you feel like an optimization genius. Then you change just one constant — from 0.1f to 0. And performance drops by 10x. No joke. No exaggeration. This very question garnered thousands of views on Stack Overflow and became a classic for anyone wanting to understand how floating-point numbers work at the processor level.

\n\n

In this article, we'll break down why such a seemingly harmless change leads to disaster, what internal processor mechanisms are responsible, and most importantly — how to avoid it. This article will be useful for both beginner C++ developers and experienced engineers who want to deepen their understanding of C++ code optimization.

\n\n

The Core Problem: Denormalized Numbers and Their Impact on C++ Performance

\n\n

First, let's reproduce the problem. Consider a simple loop that does some floating-point calculations:

\n\n
#include <iostream>\n#include <chrono>\n\nvoid slow_function(float factor) {\n    float sum = 0.0f;\n    for (int i = 0; i < 100000000; ++i) {\n        sum += factor * 0.1f;  // Fast if factor = 0.1f\n        // sum += factor * 0;  // Slow if factor = 0\n    }\n    std::cout << sum << std::endl;\n}\n\nint main() {\n    auto start = std::chrono::high_resolution_clock::now();\n    slow_function(0.1f);  // Try replacing with 0\n    auto end = std::chrono::high_resolution_clock::now();\n    std::cout << "Time: " \n              << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() \n              << " ms" << std::endl;\n    return 0;\n}
\n\n

If you run this code with factor = 0.1f, the execution time will be, say, 100 ms. Replace it with factor = 0 — and you'll get 1000 ms or more. A 10x difference. Why?

\n\n

The answer lies in denormalized numbers (denormals). This is one of the most insidious pitfalls in C++ optimization that few people know about until they encounter it firsthand.

\n\n

What Are Denormalized Numbers and Why Do They Slow Down Code?

\n\n

The IEEE 754 standard for floating-point numbers defines not only normal numbers (where the mantissa starts with 1) but also denormalized (subnormal) numbers. They are used to represent very small values close to zero. When a computation result becomes smaller than the minimum normal number, the processor switches to denormal processing mode.

\n\n

The problem is that hardware support for denormals in x86 processors is extremely slow. While normal float operations take 1-3 clock cycles, operations with denormals can take 100-200 clock cycles or more. This is the cause of the performance drop.

\n\n

Let's return to our example. When factor = 0.1f, all intermediate results remain in the normal range. But when factor = 0, multiplication by zero yields zero. Then we add 0.1f to zero — we get 0.1f. But on the next iteration, we multiply by zero again and get zero again. And so on millions of times. As a result, the processor constantly generates denormalized values, leading to catastrophic slowdown.

\n\n

Experiment: See the Difference for Yourself

\n\n

Let's write a more illustrative example that shows how denormals affect C++ code performance:

\n\n
#include <iostream>\n#include <chrono>\n#include <cmath>\n\nvoid test_performance(float initial_value) {\n    float x = initial_value;\n    auto start = std::chrono::high_resolution_clock::now();\n    \n    for (int i = 0; i < 100000000; ++i) {\n        x = x * 0.5f + 0.1f;  // Typical recurrence formula\n    }\n    \n                

Blogs

Book Recommendations