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

stillwater-sc / universal / 24956271367

26 Apr 2026 12:04PM UTC coverage: 84.299% (-0.001%) from 84.3%
24956271367

Pull #772

github

web-flow
Merge 33d58a242 into a18a12f84
Pull Request #772: feat(math): add constexpr sqrt to sw::math::constexpr_math

82 of 97 new or added lines in 2 files covered. (84.54%)

4 existing lines in 2 files now uncovered.

45431 of 53893 relevant lines covered (84.3%)

6353662.69 hits per line

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

89.66
/include/sw/math/constexpr_math/sqrt.hpp
1
#pragma once
2
// sqrt.hpp: constexpr square root for IEEE-754 float and double
3
//
4
// Copyright (C) 2017 Stillwater Supercomputing, Inc.
5
// SPDX-License-Identifier: MIT
6
//
7
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
8
//
9
// -----------------------------------------------------------------------------
10
// Algorithm
11
// -----------------------------------------------------------------------------
12
// Decompose x = 2^E * M with M in [1, 2). If E is odd, absorb a factor of 2
13
// into M (giving M in [2, 4)) and decrement E so it becomes even. Then
14
//
15
//     sqrt(x) = sqrt(M) * 2^(E/2)
16
//
17
// where 2^(E/2) is constructed exactly via detail::pow2 and sqrt(M) is
18
// computed via Newton's iteration
19
//
20
//     y_{n+1} = 0.5 * (y_n + M / y_n)
21
//
22
// which has quadratic convergence. From a constant initial guess of 1.5
23
// (worst-case ~2 bits accurate over M in [1, 4)), six iterations reach
24
// ~96 bits of precision -- well past double's 53-bit significand. Two extra
25
// over the strict minimum keeps us safe against the rounding mode of any
26
// individual multiply/add.
27
//
28
// Self-contained: only depends on detail::pow2 (already in the facility) and
29
// std::bit_cast.
30

31
#include <bit>
32
#include <cstdint>
33
#include <limits>
34

35
#include <math/constexpr_math/detail.hpp>
36

37
namespace sw { namespace math { namespace constexpr_math {
38

39
// constexpr sqrt(double): returns the square root of a.
40
// Special values follow IEEE-754 conventions:
41
//   sqrt(NaN)   -> NaN
42
//   sqrt(x<0)   -> NaN  (including -inf)
43
//   sqrt(+/-0)  -> +/-0  (sign preserved)
44
//   sqrt(+inf)  -> +inf
45
constexpr double sqrt(double a) {
34✔
46
        if (a != a) return a;                                                // NaN propagation
34✔
47
        if (a < 0.0) return std::numeric_limits<double>::quiet_NaN();
34✔
48
        if (a == 0.0) return a;                                              // +/-0 preserved
34✔
49
        if (a == std::numeric_limits<double>::infinity()) return a;
34✔
50

51
        // Decompose a = 2^E * M with M in [1, 2). Mirrors log2/exp2's bit handling.
52
        std::uint64_t bits = std::bit_cast<std::uint64_t>(a);
34✔
53
        int exp_field = static_cast<int>((bits >> 52) & 0x7FFu);
34✔
54

55
        int subnormal_shift = 0;
34✔
56
        if (exp_field == 0) {
34✔
57
                std::uint64_t mant = bits & ((std::uint64_t{1} << 52) - 1);
1✔
58
                while ((mant & (std::uint64_t{1} << 52)) == 0) {
3✔
59
                        mant <<= 1;
2✔
60
                        ++subnormal_shift;
2✔
61
                }
62
                bits = mant;
1✔
63
                exp_field = 1;
1✔
64
        }
65

66
        int E = exp_field - 1023 - subnormal_shift;
34✔
67
        std::uint64_t m_bits = (bits & ((std::uint64_t{1} << 52) - 1))
34✔
68
                               | (std::uint64_t{1023} << 52);
34✔
69
        double M = std::bit_cast<double>(m_bits);                            // M in [1, 2)
34✔
70

71
        // If E is odd, absorb a factor of 2 into M so 2^(E/2) is an integer power.
72
        // Bitwise & 1 correctly identifies odd magnitudes for both signs in two's
73
        // complement; E -= 1 brings odd-positive down to even and odd-negative
74
        // further down to even.
75
        if (E & 1) {
34✔
76
                M *= 2.0;
17✔
77
                E -= 1;
17✔
78
        }
79
        // Now E is even and M is in [1, 4).
80

81
        // Newton's iteration for sqrt(M). Quadratic convergence: starting from
82
        // 1.5 (worst-case error ~2 bits over [1, 4)), each iteration roughly
83
        // doubles the bit-precision: 2 -> 4 -> 8 -> 16 -> 32 -> 64 -> 128.
84
        // Six iterations reach well past double's 53-bit significand.
85
        double y = 1.5;
34✔
86
        y = 0.5 * (y + M / y);
34✔
87
        y = 0.5 * (y + M / y);
34✔
88
        y = 0.5 * (y + M / y);
34✔
89
        y = 0.5 * (y + M / y);
34✔
90
        y = 0.5 * (y + M / y);
34✔
91
        y = 0.5 * (y + M / y);
34✔
92

93
        return y * detail::pow2(E / 2);
34✔
94
}
95

96
// constexpr sqrt(float): same algorithm at lower precision.
97
// 4 Newton iterations from 1.5 reach ~32 bits, well past float's 24-bit
98
// significand.
99
constexpr float sqrt(float a) {
10✔
100
        if (a != a) return a;
10✔
101
        if (a < 0.0f) return std::numeric_limits<float>::quiet_NaN();
10✔
102
        if (a == 0.0f) return a;
10✔
103
        if (a == std::numeric_limits<float>::infinity()) return a;
10✔
104

105
        std::uint32_t bits = std::bit_cast<std::uint32_t>(a);
10✔
106
        int exp_field = static_cast<int>((bits >> 23) & 0xFFu);
10✔
107

108
        int subnormal_shift = 0;
10✔
109
        if (exp_field == 0) {
10✔
NEW
110
                std::uint32_t mant = bits & ((std::uint32_t{1} << 23) - 1);
×
NEW
111
                while ((mant & (std::uint32_t{1} << 23)) == 0) {
×
NEW
112
                        mant <<= 1;
×
NEW
113
                        ++subnormal_shift;
×
114
                }
NEW
115
                bits = mant;
×
NEW
116
                exp_field = 1;
×
117
        }
118

119
        int E = exp_field - 127 - subnormal_shift;
10✔
120
        std::uint32_t m_bits = (bits & ((std::uint32_t{1} << 23) - 1))
10✔
121
                               | (std::uint32_t{127} << 23);
10✔
122
        float M = std::bit_cast<float>(m_bits);
10✔
123

124
        if (E & 1) {
10✔
125
                M *= 2.0f;
5✔
126
                E -= 1;
5✔
127
        }
128

129
        float y = 1.5f;
10✔
130
        y = 0.5f * (y + M / y);
10✔
131
        y = 0.5f * (y + M / y);
10✔
132
        y = 0.5f * (y + M / y);
10✔
133
        y = 0.5f * (y + M / y);
10✔
134

135
        return y * detail::pow2f(E / 2);
10✔
136
}
137

138
}}}  // namespace sw::math::constexpr_math
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