• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stillwater-sc / universal / 24943701244

25 Apr 2026 11:38PM UTC coverage: 84.334% (-0.003%) from 84.337%
24943701244

Pull #762

github

web-flow
Merge 25d579835 into 939db92ae
Pull Request #762: feat(math): add sw::math::constexpr_math facility with constexpr log2

90 of 104 new or added lines in 2 files covered. (86.54%)

17 existing lines in 2 files now uncovered.

45106 of 53485 relevant lines covered (84.33%)

6403110.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.76
/internal/constexpr_math/api/log2.cpp
1
// log2.cpp: regression for sw::math::constexpr_math::log2(float|double)
2
//
3
// Copyright (C) 2017 Stillwater Supercomputing, Inc.
4
// SPDX-License-Identifier: MIT
5
//
6
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
7

8
#include <cmath>
9
#include <iostream>
10
#include <iomanip>
11
#include <limits>
12

13
#include <math/constexpr_math.hpp>
14

15
namespace cm = sw::math::constexpr_math;
16

17
// ----------------------------------------------------------------------------
18
// Compile-time correctness via static_assert
19
// ----------------------------------------------------------------------------
20

21
// Exact powers of 2 -- the integer-exponent path, must be bit-exact.
22
static_assert(cm::log2(1.0)    == 0.0, "log2(1) == 0");
23
static_assert(cm::log2(2.0)    == 1.0, "log2(2) == 1");
24
static_assert(cm::log2(4.0)    == 2.0, "log2(4) == 2");
25
static_assert(cm::log2(8.0)    == 3.0, "log2(8) == 3");
26
static_assert(cm::log2(0.5)    == -1.0, "log2(0.5) == -1");
27
static_assert(cm::log2(0.25)   == -2.0, "log2(0.25) == -2");
28
static_assert(cm::log2(1024.0) == 10.0, "log2(1024) == 10");
29

30
// Float overload, exact powers of 2
31
static_assert(cm::log2(1.0f) == 0.0f, "log2f(1) == 0");
32
static_assert(cm::log2(8.0f) == 3.0f, "log2f(8) == 3");
33
static_assert(cm::log2(0.5f) == -1.0f, "log2f(0.5) == -1");
34

35
// Special values
36
static_assert(cm::log2(0.0)  == -std::numeric_limits<double>::infinity(),
37
              "log2(0) == -inf");
38
static_assert(cm::log2(0.0f) == -std::numeric_limits<float>::infinity(),
39
              "log2f(0) == -inf");
40
static_assert(cm::log2(std::numeric_limits<double>::infinity())
41
              == std::numeric_limits<double>::infinity(),
42
              "log2(+inf) == +inf");
43

44
// log2(x) for x < 0 must yield NaN. NaN != NaN, so the test is `result != result`.
45
static_assert(cm::log2(-1.0) != cm::log2(-1.0), "log2(-1) == NaN");
46
static_assert(cm::log2(-2.5f) != cm::log2(-2.5f), "log2f(-2.5) == NaN");
47

48
// Acceptance form for issue #423: enables `constexpr takum16 t = 3.14f;`
49
// log2(3.14) = ln(3.14) / ln(2) ~= 1.65076455911...
50
constexpr double cx_log2_3p14 = cm::log2(3.14);
51
static_assert(cx_log2_3p14 > 1.65076 && cx_log2_3p14 < 1.65077,
52
              "log2(3.14) ~= 1.65076455...");
53

54
// Acceptance: enables compile-time lns encoding from a float literal.
55
// For lns<8,4>, log2(0.75) is needed in the value->encoding mapping.
56
constexpr double cx_log2_0p75 = cm::log2(0.75);
57
static_assert(cx_log2_0p75 > -0.4151 && cx_log2_0p75 < -0.4149,
58
              "log2(0.75) ~= -0.41504");
59

60
// ----------------------------------------------------------------------------
61
// Runtime cross-check vs std::log2 over a representative grid
62
// ----------------------------------------------------------------------------
63

64
int main() {
1✔
65
        std::cout << "sw::math::constexpr_math::log2 verification\n";
1✔
66

67
        int errors = 0;
1✔
68
        auto check = [&](const char* name, double our, double ref, double tol) {
24✔
69
                double err = (ref == 0.0) ? std::abs(our) : std::abs((our - ref) / ref);
24✔
70
                if (err > tol) {
24✔
NEW
71
                        ++errors;
×
72
                        std::cout << "FAIL " << name
NEW
73
                                  << "  our=" << std::setprecision(17) << our
×
NEW
74
                                  << "  ref=" << ref
×
NEW
75
                                  << "  rel-err=" << err << '\n';
×
76
                }
77
        };
25✔
78

79
        // Sweep across a wide dynamic range and a fine-grained set inside [1, 2).
80
        const double points[] = {
1✔
81
                0.5, 0.75, 1.0, 1.25, 1.5, 1.7320508, 1.9999999,
82
                2.0, 3.0, 3.14, 5.0, 7.0, 10.0, 100.0, 1024.0,
83
                1e-10, 1e-5, 1e-3, 1e3, 1e10, 1e100, 1e-300, 1e300,
84
        };
85
        for (double x : points) {
24✔
86
                double our = cm::log2(x);
23✔
87
                double ref = std::log2(x);
23✔
88
                // Allow up to 4 ulp (double eps ~2.2e-16, so ~1e-15)
89
                check("double sweep", our, ref, 1e-15);
23✔
90
        }
91

92
        // Subnormal sample
93
        {
94
                double sub = std::numeric_limits<double>::min() / 4.0;  // subnormal
1✔
95
                double our = cm::log2(sub);
1✔
96
                double ref = std::log2(sub);
1✔
97
                check("double subnormal", our, ref, 1e-14);
1✔
98
        }
99

100
        // Float sweep
101
        const float fpoints[] = {
1✔
102
                0.5f, 0.75f, 1.0f, 1.5f, 2.0f, 3.14f, 1024.0f, 1e10f, 1e-10f,
103
        };
104
        for (float x : fpoints) {
10✔
105
                float our = cm::log2(x);
9✔
106
                float ref = std::log2(x);
9✔
107
                double err = (ref == 0.0f) ? std::abs(our) : std::abs((our - ref) / ref);
9✔
108
                // Float epsilon ~1.19e-7; allow 1e-6 relative.
109
                if (err > 1e-6) {
9✔
NEW
110
                        ++errors;
×
NEW
111
                        std::cout << "FAIL float sweep  x=" << x
×
NEW
112
                                  << "  our=" << our << "  ref=" << ref
×
NEW
113
                                  << "  rel-err=" << err << '\n';
×
114
                }
115
        }
116

117
        std::cout << "constexpr_math::log2: " << (errors == 0 ? "PASS" : "FAIL")
1✔
118
                  << " (" << errors << " error" << (errors == 1 ? "" : "s") << ")\n";
1✔
119
        return (errors == 0) ? 0 : 1;
1✔
120
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc