Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

C++  Module 2 – Types, values and expressions

Floating-point precision and comparison in C++

Why 0.1 + 0.2 is not 0.3 in C++, how a double is stored, how to compare with a tolerance, print digits that round-trip, and keep money exact.

  • Beginner
  • 22 minutes
  • Examples run with Clang 21.0.0, GCC 16.2.0 and Python 3.14.8
  • By MySmartCoPilot
Free: 22 minutes

What you will learn

  • Explain why 0.1 + 0.2 != 0.3 in binary floating point
  • Compare floating-point values with a tolerance
  • Format values with enough digits to round-trip

Before you start

What you’ll learn · Before you start

What you will learn

  • Explain why 0.1 + 0.2 != 0.3 in binary floating point
  • Compare floating-point values with a tolerance
  • Format values with enough digits to round-trip

Before you start

On this page

A double stores a number in binary: on every mainstream platform it is the IEEE 754 binary64 format, with 53 significant bits and an exponent. Most decimal fractions, including 0.1, 0.2 and 0.3, have no exact binary form, so each one is stored as the nearest double. The sum of the stored 0.1 and the stored 0.2 then lands one step above the double nearest 0.3, and 0.1 + 0.2 == 0.3 is false. Nothing is broken, and C++ is not alone: every language that uses this hardware format gets the same answer. The cure is to use floating point knowingly: compare with a tolerance instead of ==, print enough digits to see what is really stored, test for NaN with std::isnan, and count money in whole cents or paise. This lesson shows each of these on Apple clang 21 and GCC 16, and measures how rounding errors grow when a loop adds up ten million values.

What a double stores

A double is 64 bits split into three fields: 1 sign bit, 11 bits of exponent and 52 bits of fraction. For a normal number the fraction follows an implied leading 1, which is how 52 stored bits give 53 significant bits.

The double 0.1 as 1 sign bit, 11 exponent bits and 52 fraction bits, which store a value a little above 0.1.The 64 bits of the double 0.1sign1 bit0exponent11 bits01111111011fraction52 bits1001 1001 … 1010sign 0: positiveexponent 1019 − bias 1023 = −4: scale by 2⁻⁴fraction 0x999999999999A, after a hidden leading 1(1 + 0x999999999999A / 2⁵²) × 2⁻⁴= 0.1000000000000000055511…

How a double stores 0.1

Text description of the diagram

A bar of 64 bits split into three fields, from left to right.

  • Sign, 1 bit: 0, so the number is positive.
  • Exponent, 11 bits: 01111111011, which is 1019. The stored exponent has a bias of 1023, so the number is scaled by 2 to the power 1019 − 1023 = −4.
  • Fraction, 52 bits: 1001 1001 … 1010, which is 0x999999999999A in hexadecimal. A leading 1 is implied and not stored.

The value is (1 + 0x999999999999A / 2⁵²) × 2⁻⁴, which is 0.1000000000000000055511…, slightly more than 0.1.

std::bit_cast from <bit> (C++20) copies the bits of a double into an integer of the same size, so a program can take the fields apart:

bits.cpp C++ · bits.cpp
#include <bit>
#include <cstdint>
#include <limits>
#include <print>

// Splits a double into the three fields of the IEEE 754 binary64 format.
void fields(double x) {
    auto bits = std::bit_cast<std::uint64_t>(x);
    auto sign = bits >> 63;
    auto exponent = (bits >> 52) & 0x7FF;              // 11 bits, stored with a bias of 1023
    auto fraction = bits & ((std::uint64_t{1} << 52) - 1);  // 52 bits after the leading 1
    std::println("{:<5} {:#018x}  sign {}  exponent {:>4} = 2^{:<3}  fraction {:#015x}",
                 x, bits, sign, exponent, static_cast<int>(exponent) - 1023, fraction);
}

int main() {
    static_assert(std::numeric_limits<double>::is_iec559);   // IEEE 754 on this platform
    fields(1.0);
    fields(0.5);
    fields(0.1);
    fields(-2.0);
}
The fields of four doubles Python · bits_session.py
from session import run

run("""
clang++ -std=c++23 -Wall -Wextra bits.cpp -o bits
./bits
g++-16 -std=c++23 -Wall -Wextra bits.cpp -o bits
./bits
""")

Output

$ clang++ -std=c++23 -Wall -Wextra bits.cpp -o bits
$ ./bits
1     0x3ff0000000000000  sign 0  exponent 1023 = 2^0    fraction 0x0000000000000
0.5   0x3fe0000000000000  sign 0  exponent 1022 = 2^-1   fraction 0x0000000000000
0.1   0x3fb999999999999a  sign 0  exponent 1019 = 2^-4   fraction 0x999999999999a
-2    0xc000000000000000  sign 1  exponent 1024 = 2^1    fraction 0x0000000000000
$ g++-16 -std=c++23 -Wall -Wextra bits.cpp -o bits
$ ./bits
1     0x3ff0000000000000  sign 0  exponent 1023 = 2^0    fraction 0x0000000000000
0.5   0x3fe0000000000000  sign 0  exponent 1022 = 2^-1   fraction 0x0000000000000
0.1   0x3fb999999999999a  sign 0  exponent 1019 = 2^-4   fraction 0x999999999999a
-2    0xc000000000000000  sign 1  exponent 1024 = 2^1    fraction 0x0000000000000

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 bits_session.py

Each $ line is a command, exactly as typed at a terminal prompt, and the lines under it are its output. 1.0, 0.5 and -2.0 are exact: each is a power of two, so the fraction is all zeros, and only the exponent and the sign change. 0.1 is 1/10, and a fraction is exact in binary only when its denominator is a power of two. 10 has a factor of 5, so 0.1 in binary is 0.000110011001100…, repeating for ever; the double keeps the first 53 significant bits and rounds the rest, which makes the repeating hexadecimal digits 999… end in a, rounded up. The stored value is a little more than 0.1. The static_assert at the start of main checks std::numeric_limits<double>::is_iec559, which is true when double is IEEE 754, so the program refuses to build anywhere this description would be wrong.

IEEE 754 Floating-Point Converter Enter 0.1, 0.2 or 0.3 to see their 64 bits and the exact decimal value each one stores.

Why 0.1 + 0.2 is not 0.3

point_one.cpp C++ · point_one.cpp
#include <cmath>
#include <iostream>
#include <print>

int main() {
    double sum = 0.1 + 0.2;
    std::println("sum == 0.3:  {}", sum == 0.3);
    std::println("{{}}:         {} {} {}", 0.1, 0.3, sum);
    std::println("{{:.17g}}:    {:.17g} {:.17g} {:.17g}", 0.1, 0.3, sum);
    std::cout << "std::cout:   " << 0.1 << ' ' << 0.3 << ' ' << sum << '\n';
    std::println("next double above 0.3: {}", std::nextafter(0.3, 1.0));
    std::println("shortest form of 1000000.0 and 1234567.0: {} {}", 1000000.0, 1234567.0);
}
Printing the same numbers three ways Python · point_one_session.py
from session import run

run("""
clang++ -std=c++23 -Wall -Wextra point_one.cpp -o point_one
./point_one
g++-16 -std=c++23 -Wall -Wextra point_one.cpp -o point_one
./point_one
""")

Output

$ clang++ -std=c++23 -Wall -Wextra point_one.cpp -o point_one
$ ./point_one
sum == 0.3:  false
{}:         0.1 0.3 0.30000000000000004
{:.17g}:    0.10000000000000001 0.29999999999999999 0.30000000000000004
std::cout:   0.1 0.3 0.3
next double above 0.3: 0.30000000000000004
shortest form of 1000000.0 and 1234567.0: 1e+06 1234567
$ g++-16 -std=c++23 -Wall -Wextra point_one.cpp -o point_one
$ ./point_one
sum == 0.3:  false
{}:         0.1 0.3 0.30000000000000004
{:.17g}:    0.10000000000000001 0.29999999999999999 0.30000000000000004
std::cout:   0.1 0.3 0.3
next double above 0.3: 0.30000000000000004
shortest form of 1000000.0 and 1234567.0: 1e+06 1234567

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 point_one_session.py

Three roundings are involved: 0.1 and 0.2 are each rounded when the program is compiled, and their sum is rounded again when it is computed. The result is 0.30000000000000004, which is exactly the next double above the one that the literal 0.3 is stored as, the value std::nextafter printed. One step apart is enough for == to say false.

The three printing styles of the session are worth knowing:

  • std::println("{}", x) prints the shortest text that reads back as exactly the same double. The standard defines it through std::to_chars: the smallest number of characters that recovers the value. That is why it shows 0.1 for 0.1 and 0.30000000000000004 for the sum: each is the shortest text for its own value. Shortest can mean scientific notation, so 1000000.0 prints as 1e+06; ask for a format such as {:.2f} when a reader needs ordinary digits.
  • {:.17g} prints 17 significant digits, which std::numeric_limits<double>::max_digits10 says is always enough to tell two double values apart. It shows that 0.1 is stored as 0.10000000000000001 and 0.3 as 0.29999999999999999.
  • std::cout prints 6 significant digits unless you set a precision, so the sum comes out as 0.3: the difference is there, but the stream rounds it away. Many confusing debugging sessions start this way.

Comparing floating-point values

Because computed values carry small rounding errors, compare them with a tolerance. A relative tolerance accepts a difference that is small compared with the numbers themselves, so it works the same for 0.3 and for 10²⁰. An absolute tolerance accepts a difference below a fixed size, which is the only test that can accept a value near zero, since nothing but zero is relatively close to zero.

compare.cpp C++ · compare.cpp
#include <algorithm>
#include <cmath>
#include <limits>
#include <print>

// True when a and b differ by at most rel times the larger of the two, or by at most abs.
// The relative test scales with the numbers; the absolute one handles values near zero.
bool nearly_equal(double a, double b, double rel = 1e-9, double abs = 1e-12) {
    if (a == b) return true;                        // also equal infinities
    if (!std::isfinite(a) || !std::isfinite(b)) return false;
    double diff = std::fabs(a - b);
    return diff <= abs || diff <= rel * std::max(std::fabs(a), std::fabs(b));
}

int main() {
    double sum = 0.1 + 0.2;
    double balance = 0.1 + 0.2 - 0.3;               // should be 0, is a tiny residue
    double inf = std::numeric_limits<double>::infinity();
    double nan = std::numeric_limits<double>::quiet_NaN();

    std::println("balance = {}", balance);
    std::println("nearly_equal(0.1 + 0.2, 0.3)            {}", nearly_equal(sum, 0.3));
    std::println("nearly_equal(balance, 0.0)              {}", nearly_equal(balance, 0.0));
    std::println("nearly_equal(balance, 0.0, 1e-9, 0.0)   {}", nearly_equal(balance, 0.0, 1e-9, 0.0));
    std::println("nearly_equal(1e20, 1e20 + 1e5)          {}", nearly_equal(1e20, 1e20 + 1e5));
    std::println("nearly_equal(1.0, 1.001)                {}", nearly_equal(1.0, 1.001));
    std::println("nearly_equal(inf, inf)                  {}", nearly_equal(inf, inf));
    std::println("nearly_equal(nan, nan)                  {}", nearly_equal(nan, nan));
}
A tolerance test on both compilers Python · compare_session.py
from session import run

run("""
clang++ -std=c++23 -Wall -Wextra compare.cpp -o compare
./compare
g++-16 -std=c++23 -Wall -Wextra compare.cpp -o compare
./compare
""")

Output

$ clang++ -std=c++23 -Wall -Wextra compare.cpp -o compare
$ ./compare
balance = 5.551115123125783e-17
nearly_equal(0.1 + 0.2, 0.3)            true
nearly_equal(balance, 0.0)              true
nearly_equal(balance, 0.0, 1e-9, 0.0)   false
nearly_equal(1e20, 1e20 + 1e5)          true
nearly_equal(1.0, 1.001)                false
nearly_equal(inf, inf)                  true
nearly_equal(nan, nan)                  false
$ g++-16 -std=c++23 -Wall -Wextra compare.cpp -o compare
$ ./compare
balance = 5.551115123125783e-17
nearly_equal(0.1 + 0.2, 0.3)            true
nearly_equal(balance, 0.0)              true
nearly_equal(balance, 0.0, 1e-9, 0.0)   false
nearly_equal(1e20, 1e20 + 1e5)          true
nearly_equal(1.0, 1.001)                false
nearly_equal(inf, inf)                  true
nearly_equal(nan, nan)                  false

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 compare_session.py

The function accepts the sum as 0.3. The residue balance, which should be 0 and is about 5.6 × 10⁻¹⁷, passes only because of the absolute tolerance: with abs set to 0, the relative test alone rejects it. 10²⁰ and 10²⁰ + 10⁵ are equal to about 15 digits, so they pass, while 1.0 and 1.001 differ in the third decimal and do not. Two infinities of the same sign compare equal, and NaN is close to nothing, not even itself.

Choose the tolerances from the problem, not from the type. A relative tolerance of 10⁻⁹ asks for about nine matching digits, plenty after a few operations on double values. The absolute tolerance should be far below anything that matters in your units: 10⁻¹² of a rupee or a metre is far below anything that can be counted or measured. Avoid std::numeric_limits<double>::epsilon() as a universal tolerance: it is the gap between 1.0 and the next double, about 2.2 × 10⁻¹⁶, and means nothing for numbers far from 1.

Errors add up

Every operation rounds its result to the nearest representable value, and in a long loop the errors accumulate. This program adds 0.1 ten million times, in a float, in a double, and with Kahan summation, which carries the part of each addition that rounding dropped into the next one:

accumulate.cpp C++ · accumulate.cpp
#include <print>

int main() {
    constexpr int n = 10'000'000;
    float in_float = 0.0f;
    double in_double = 0.0;
    double kahan = 0.0;
    double lost = 0.0;            // Kahan summation: the low-order part each addition dropped
    for (int i = 0; i < n; ++i) {
        in_float += 0.1f;
        in_double += 0.1;
        double y = 0.1 - lost;
        double t = kahan + y;
        lost = (t - kahan) - y;
        kahan = t;
    }
    std::println("0.1 added {} times:", n);
    std::println("float:  {:.6f}", in_float);
    std::println("double: {:.10f}", in_double);
    std::println("Kahan:  {:.10f}", kahan);
}
Ten million additions of 0.1 Python · accumulate_session.py
from session import run

run("""
clang++ -std=c++23 -Wall -Wextra accumulate.cpp -o accumulate
./accumulate
g++-16 -std=c++23 -Wall -Wextra accumulate.cpp -o accumulate
./accumulate
""")

Output

$ clang++ -std=c++23 -Wall -Wextra accumulate.cpp -o accumulate
$ ./accumulate
0.1 added 10000000 times:
float:  1087937.000000
double: 999999.9998389754
Kahan:  1000000.0000000000
$ g++-16 -std=c++23 -Wall -Wextra accumulate.cpp -o accumulate
$ ./accumulate
0.1 added 10000000 times:
float:  1087937.000000
double: 999999.9998389754
Kahan:  1000000.0000000000

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 accumulate_session.py

The exact answer is 1,000,000. The float total is 1,087,937, almost 9 % too high. A float has 24 significant bits, so the gap between neighbouring float values grows with the total: above 524,288 (2¹⁹) it is 0.0625, and each 0.1 added is rounded to the nearest multiple of that gap, which is 0.125. From that point every addition counts 25 % too much. The double total misses by about 0.00016, because its 53 bits make the gaps about 500 million times smaller. Kahan summation recovers the exact result at the cost of four operations per value instead of one.

Two cautions go with it. -ffast-math lets the compiler reorder floating-point operations as if they were exact, which GCC’s manual warns can change results, and reordering is exactly what turns Kahan’s correction into zero, so code that relies on it must not be built that way. And for one-off sums the simplest fix is often a wider type: add float data in a double.

Infinity, NaN and signed zero

IEEE 754 has values that are not ordinary numbers, and C++ exposes them through std::numeric_limits:

special.cpp C++ · special.cpp
#include <cmath>
#include <limits>
#include <print>

int main() {
    const double inf = std::numeric_limits<double>::infinity();
    const double nan = std::numeric_limits<double>::quiet_NaN();

    std::println("x    | x == x | x != x | x < 1 | x > 1 | std::isnan(x)");
    for (double x : {1.0, inf, nan}) {
        std::println("{:<4} | {:<6} | {:<6} | {:<5} | {:<5} | {}", x, x == x, x != x, x < 1, x > 1, std::isnan(x));
    }
    std::println("");
    std::println("inf > max(): {}, 1 / inf = {}", inf > std::numeric_limits<double>::max(), 1 / inf);
    std::println("-0.0 == 0.0: {}, but std::signbit(-0.0) is {}", -0.0 == 0.0, std::signbit(-0.0));
    std::println("smallest positive double: {}", std::numeric_limits<double>::denorm_min());
}
Infinity and NaN in comparisons Python · special_session.py
from session import run

run("""
clang++ -std=c++23 -Wall -Wextra special.cpp -o special
./special
g++-16 -std=c++23 -Wall -Wextra special.cpp -o special
./special
""")

Output

$ clang++ -std=c++23 -Wall -Wextra special.cpp -o special
$ ./special
x    | x == x | x != x | x < 1 | x > 1 | std::isnan(x)
1    | true   | false  | false | false | false
inf  | true   | false  | false | true  | false
nan  | false  | true   | false | false | true

inf > max(): true, 1 / inf = 0
-0.0 == 0.0: true, but std::signbit(-0.0) is true
smallest positive double: 5e-324
$ g++-16 -std=c++23 -Wall -Wextra special.cpp -o special
$ ./special
x    | x == x | x != x | x < 1 | x > 1 | std::isnan(x)
1    | true   | false  | false | false | false
inf  | true   | false  | false | true  | false
nan  | false  | true   | false | false | true

inf > max(): true, 1 / inf = 0
-0.0 == 0.0: true, but std::signbit(-0.0) is true
smallest positive double: 5e-324

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 special_session.py

  • Infinity is larger than every finite value, and 1 / inf is 0.
  • NaN, “not a number”, is the result of operations that have no sensible answer, such as infinity minus infinity. Every ordered comparison with a NaN is false and == is false even with itself, so the table’s NaN row is false everywhere except in x != x. Test for it with std::isnan(x), and for “an ordinary number” with std::isfinite(x). A NaN in a sort key or a std::set breaks the ordering those containers depend on.
  • -0.0 compares equal to 0.0, but std::signbit can tell them apart.
  • Below the smallest normal value lie the subnormal numbers, which give up precision to reach as low as about 4.9 × 10⁻³²⁴.

Create infinities with std::numeric_limits<double>::infinity(), not with 1.0 / 0.0: the C++ standard says division by zero is undefined for every arithmetic type, even though IEEE 754 hardware returns an infinity, and Clang’s UBSan can report it with -fsanitize=float-divide-by-zero.

Money: count in the smallest unit

Accounts and prices are decimal, and a decimal amount such as 0.10 is not exact in binary, so money stored as a double drifts. Store whole units of the smallest coin instead, cents or paise, in a 64-bit integer:

money.cpp C++ · money.cpp
#include <cstdint>
#include <print>

int main() {
    // One hundred million payments of 0.10, added as a double and as whole paise.
    double total = 0.0;
    std::int64_t total_paise = 0;
    for (int i = 0; i < 100'000'000; ++i) {
        total += 0.10;
        total_paise += 10;
    }
    std::println("double: {:.2f} (stored as {})", total, total);
    std::println("paise:  {}.{:02}", total_paise / 100, total_paise % 100);

    // 7.5 % interest a year for 10 years on 100000.00, rounded to the nearest paisa every year.
    std::int64_t balance = 100'000'00;   // in paise
    for (int year = 1; year <= 10; ++year) {
        balance += (balance * 75 + 500) / 1000;   // 7.5 % = 75 / 1000, with +500 to round half up
    }
    std::println("after 10 years: {}.{:02}", balance / 100, balance % 100);
}
A hundred million payments of 0.10 Python · money_session.py
from session import run

run("""
clang++ -std=c++23 -Wall -Wextra money.cpp -o money
./money
g++-16 -std=c++23 -Wall -Wextra money.cpp -o money
./money
""")

Output

$ clang++ -std=c++23 -Wall -Wextra money.cpp -o money
$ ./money
double: 9999999.98 (stored as 9999999.98112945)
paise:  10000000.00
after 10 years: 206103.16
$ g++-16 -std=c++23 -Wall -Wextra money.cpp -o money
$ ./money
double: 9999999.98 (stored as 9999999.98112945)
paise:  10000000.00
after 10 years: 206103.16

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 money_session.py

After one hundred million payments of 0.10, the double total is 9,999,999.98112945, and rounded to two decimals the total is two paise short of the 10,000,000.00 that the integer count gives exactly. A std::int64_t holds about 9.2 × 10¹⁸ paise, roughly 92 quadrillion rupees, so range is no concern. Integer money also makes rounding a decision you write down: the interest calculation rounds to the nearest paisa every year, half up, by adding 500 before the division by 1,000. Ten years at 7.5 % turn 100,000.00 into 206,103.16, and anyone can reproduce that figure paisa for paisa, which is the property an account needs.

Rounding Calculator See how 206103.1562… rounds in each rounding mode, before you choose one for your amounts.

Narrower and wider types

float has 24 significant bits, about 7 decimal digits, and saves memory in large arrays. long double depends on the platform: on Apple silicon it is identical to double, which Apple documents as a difference from the standard Arm ABI. C++23 adds the optional types of <stdfloat> from proposal P1467R9, such as std::float16_t, a 16-bit type used in graphics and machine learning:

half_precision.cpp C++ · half_precision.cpp
#include <limits>
#include <print>
#if __has_include(<stdfloat>)
#include <stdfloat>
#endif

int main() {
    std::println("long double: {} bytes, {} significant bits", sizeof(long double), std::numeric_limits<long double>::digits);
#if defined(__STDCPP_FLOAT16_T__)
    std::float16_t h = 0.1f16;
    std::println("std::float16_t: {} bytes; 0.1 is stored as {}", sizeof(h), static_cast<double>(h));
#else
    std::println("no std::float16_t in this standard library");
#endif
}
long double and std::float16_t Python · half_precision_session.py
from session import run

run("""
clang++ -std=c++23 -Wall -Wextra half_precision.cpp -o half_precision
./half_precision
g++-16 -std=c++23 -Wall -Wextra half_precision.cpp -o half_precision
./half_precision
""")

Output

$ clang++ -std=c++23 -Wall -Wextra half_precision.cpp -o half_precision
$ ./half_precision
long double: 8 bytes, 53 significant bits
no std::float16_t in this standard library
$ g++-16 -std=c++23 -Wall -Wextra half_precision.cpp -o half_precision
$ ./half_precision
long double: 8 bytes, 53 significant bits
std::float16_t: 2 bytes; 0.1 is stored as 0.0999755859375

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 half_precision_session.py

Both compilers report an 8-byte long double with 53 significant bits, the same as double. GCC 16 provides std::float16_t, and its 11 significant bits store 0.1 as 0.0999755859375, correct to only about three digits. Apple clang 21’s libc++ has no <stdfloat> header, and libc++’s status page shows P1467R9 as not implemented, so the file checks with __has_include and the macro __STDCPP_FLOAT16_T__ before using it.

Interview questions

Fresher · Why is 0.1 + 0.2 == 0.3 false in C++?

double is a binary format, and 0.1, 0.2 and 0.3 have no exact binary representation, so each literal is stored as the nearest double: 0.1 and 0.2 slightly above their true values, 0.3 slightly below. Their sum is rounded once more, and the result is the double just above the one stored for 0.3, so == compares two different values and says false. std::println("{}", 0.1 + 0.2) shows 0.30000000000000004. The fix is a comparison with a tolerance, relative for ordinary values plus an absolute one near zero, and integer cents or paise when the values are money. It is not a C++ quirk: any language that uses IEEE 754 doubles gives the same result.

Check yourself

6 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 6 What does this print?

    Read the code, then choose one answer.

    std::println("{}", 0.1 + 0.2);
    Show the answer to question 1

    Answer: 0.30000000000000004

    The sum is the double just above the one stored for 0.3, and {} prints the shortest text that reads back as exactly that double, which needs 17 significant digits here.

  2. Question 2 of 6 And this?

    Read the code, then choose one answer.

    std::cout << 0.1 + 0.2 << '\n';
    Show the answer to question 2

    Answer: 0.3

    Streams print 6 significant digits by default, so the sum is rounded to 0.3 on output. The value itself is still not equal to 0.3; std::setprecision(17) or std::println shows the difference.

  3. Question 3 of 6 Which test is true exactly when the double x is a NaN?

    Choose one answer.

    Show the answer to question 3

    Answer: std::isnan(x)

    A NaN compares unequal to everything, itself included, so both == tests are always false, and every ordered comparison with a NaN is false. x != x also works, but std::isnan says what it means.

  4. Question 4 of 6 Why does a good comparison function need an absolute tolerance as well as a relative one?

    Choose one answer.

    Show the answer to question 4

    Answer: Near zero, no value except 0 is within a relative tolerance of 0

    A relative test accepts a difference that is small compared with the larger value. When one side is 0, that allowance is 0, so a residue such as 5.6 × 10⁻¹⁷ is rejected. An absolute tolerance accepts it.

  5. Question 5 of 6 Which is the best choice for an account balance?

    Choose one answer.

    Show the answer to question 5

    Answer: A std::int64_t count of the smallest unit, such as paise or cents

    Decimal amounts such as 0.10 are not exact in binary, so binary floating point drifts and cannot round the way accounts must. Whole paise or cents in a 64-bit integer are exact, and the code states each rounding.

  6. Question 6 of 6 Adding 0.1f to a float ten million times gives roughly which total?

    Choose one answer.

    Show the answer to question 6

    Answer: 1,087,937, almost 9 % too much

    Once the total passes 524,288 the gap between neighbouring floats is 0.0625, so each 0.1 is rounded to 0.125 and every addition counts 25 % too much. A double total is off by only about 0.00016.

Key takeaways

  • double is IEEE 754 binary64 here: 1 sign bit, 11 exponent bits, 52 fraction bits, 53 significant bits.
  • Decimal fractions such as 0.1 are rounded to the nearest double, so 0.1 + 0.2 is the double just above 0.3.
  • {} prints the shortest text that reads back exactly, {:.17g} always distinguishes two doubles, and std::cout rounds to 6 digits by default.
  • Compare with a relative tolerance plus an absolute one near zero, never with == and never with epsilon() alone.
  • Rounding errors accumulate: ten million additions of 0.1 gave 1,087,937 in a float; Kahan summation or a wider type fixes it, and -ffast-math can undo Kahan.
  • NaN is unequal to everything, itself included: use std::isnan. Make infinities with numeric_limits.
  • Keep money as whole cents or paise in std::int64_t, and write the rounding rule into the code.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.