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

stillwater-sc / universal / 24947791505

26 Apr 2026 04:00AM UTC coverage: 84.346% (-0.008%) from 84.354%
24947791505

Pull #771

github

web-flow
Merge 46646abb0 into 2f0dbeac9
Pull Request #771: feat(math): add constexpr log (natural logarithm) to sw::math::constexpr_math

33 of 42 new or added lines in 2 files covered. (78.57%)

13 existing lines in 3 files now uncovered.

45244 of 53641 relevant lines covered (84.35%)

6388218.85 hits per line

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

70.0
/internal/constexpr_math/api/log.cpp
1
// log.cpp: regression for sw::math::constexpr_math::log (natural logarithm)
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
// Anchors derivable from the algorithm: log(1) = 0 (independent of implementation).
22
static_assert(cm::log(1.0)  == 0.0, "log(1) == 0");
23
static_assert(cm::log(1.0f) == 0.0f, "logf(1) == 0");
24

25
// log(2) ~= ln(2) ~= 0.6931471805599453 (the LN2 constant itself)
26
constexpr double cx_log2 = cm::log(2.0);
27
static_assert(cx_log2 > 0.69314718 && cx_log2 < 0.69314719,
28
              "log(2) ~= 0.6931471805599453");
29

30
// log(e) ~= 1 (within ~1 ulp transcendental composition error)
31
constexpr double cx_loge = cm::log(2.71828182845904523536);
32
static_assert(cx_loge > 0.99999999 && cx_loge < 1.00000001,
33
              "log(e) ~= 1");
34

35
// log(10) ~= 2.302585092994046
36
constexpr double cx_log10 = cm::log(10.0);
37
static_assert(cx_log10 > 2.3025850 && cx_log10 < 2.3025851,
38
              "log(10) ~= 2.302585092994046");
39

40
// Cross-check: log(8) ~= log(2) * 3 (within a few ulp).
41
// Tolerance instead of exact equality so a future refactor of log2's internal
42
// math cannot break this regression for a sub-ulp reason.
43
constexpr double cx_log8 = cm::log(8.0);
44
constexpr double cx_3log2 = 3.0 * cm::log(2.0);
45
constexpr double cx_delta = (cx_log8 > cx_3log2) ? (cx_log8 - cx_3log2)
46
                                                  : (cx_3log2 - cx_log8);
47
static_assert(cx_delta < 1e-15, "log(8) ~= 3 * log(2)");
48

49
// Special values (inherited from log2)
50
static_assert(cm::log(0.0) == -std::numeric_limits<double>::infinity(),
51
              "log(0) == -inf");
52
static_assert(cm::log(-0.0) == -std::numeric_limits<double>::infinity(),
53
              "log(-0) == -inf (signed zero hits the same branch)");
54
static_assert(cm::log(0.0f) == -std::numeric_limits<float>::infinity(),
55
              "logf(0) == -inf");
56
static_assert(cm::log(-0.0f) == -std::numeric_limits<float>::infinity(),
57
              "logf(-0) == -inf");
58
static_assert(cm::log(std::numeric_limits<double>::infinity())
59
              == std::numeric_limits<double>::infinity(),
60
              "log(+inf) == +inf");
61
static_assert(cm::log(-1.0) != cm::log(-1.0), "log(-1) == NaN");
62
static_assert(cm::log(-2.5f) != cm::log(-2.5f), "logf(-2.5) == NaN");
63
static_assert(cm::log(std::numeric_limits<double>::quiet_NaN())
64
              != cm::log(std::numeric_limits<double>::quiet_NaN()),
65
              "log(NaN) == NaN");
66

67
// ============================================================================
68
// Runtime cross-check vs std::log
69
// ============================================================================
70

71
int main() {
1✔
72
        std::cout << "sw::math::constexpr_math::log verification\n";
1✔
73

74
        int errors = 0;
1✔
75
        auto check = [&](const char* name, double x, double our, double ref, double tol) {
22✔
76
                double err = (ref == 0.0) ? std::abs(our) : std::abs((our - ref) / ref);
22✔
77
                if (err > tol) {
22✔
NEW
78
                        ++errors;
×
79
                        std::cout << "FAIL " << name
NEW
80
                                  << "  x=" << std::setprecision(17) << x
×
NEW
81
                                  << "  our=" << our
×
NEW
82
                                  << "  ref=" << ref
×
NEW
83
                                  << "  rel-err=" << err << '\n';
×
84
                }
85
        };
23✔
86

87
        // Sweep over a representative dynamic range.
88
        const double points[] = {
1✔
89
                1e-300, 1e-100, 1e-10, 1e-5, 0.001, 0.1, 0.5, 0.999, 1.0, 1.001,
90
                1.5, 2.0, 2.71828182845904523536, 3.14159265358979323846,
91
                7.0, 10.0, 100.0, 1024.0, 1e3, 1e10, 1e100, 1e300,
92
        };
93
        for (double x : points) {
23✔
94
                double our = cm::log(x);
22✔
95
                double ref = std::log(x);
22✔
96
                check("double sweep", x, our, ref, 1e-15);
22✔
97
        }
98

99
        // Float sweep
100
        const float fpoints[] = {
1✔
101
                1e-30f, 1e-10f, 0.001f, 0.5f, 1.0f, 2.0f, 2.71828183f, 10.0f, 1e10f, 1e30f,
102
        };
103
        for (float x : fpoints) {
11✔
104
                float our = cm::log(x);
10✔
105
                float ref = std::log(x);
10✔
106
                double err = (ref == 0.0f) ? std::abs(our) : std::abs((our - ref) / ref);
10✔
107
                if (err > 1e-6) {
10✔
NEW
108
                        ++errors;
×
NEW
109
                        std::cout << "FAIL float sweep  x=" << x
×
NEW
110
                                  << "  our=" << our << "  ref=" << ref
×
NEW
111
                                  << "  rel-err=" << err << '\n';
×
112
                }
113
        }
114

115
        std::cout << "constexpr_math::log: " << (errors == 0 ? "PASS" : "FAIL")
1✔
116
                  << " (" << errors << " error" << (errors == 1 ? "" : "s") << ")\n";
1✔
117
        return (errors == 0) ? 0 : 1;
1✔
118
}
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