Skip to content

Commit 63c99da

Browse files
authored
Merge pull request #216 from cppalliance/212
Add standard library of float functions
2 parents 6b1a95a + 4d57918 commit 63c99da

19 files changed

Lines changed: 2152 additions & 10 deletions

include/boost/safe_numbers.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include <boost/safe_numbers/integer_utilities.hpp>
2121
#include <boost/safe_numbers/byte_conversions.hpp>
2222
#include <boost/safe_numbers/numeric.hpp>
23+
#include <boost/safe_numbers/cmath.hpp>
24+
#include <boost/safe_numbers/functional.hpp>
2325

2426
#undef BOOST_SAFE_NUMBERS_DETAIL_INT128_ALLOW_SIGN_CONVERSION
2527

include/boost/safe_numbers/charconv.hpp

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
namespace boost::charconv {
1919

20-
template <safe_numbers::detail::library_type T>
20+
template <safe_numbers::detail::integral_library_type T>
2121
BOOST_SAFE_NUMBERS_HOST_DEVICE constexpr auto from_chars(const char* first, const char* last, T& value, int base = 10)
2222
-> charconv::from_chars_result
2323
{
@@ -30,14 +30,43 @@ BOOST_SAFE_NUMBERS_HOST_DEVICE constexpr auto from_chars(const char* first, cons
3030
return r;
3131
}
3232

33-
template <safe_numbers::detail::library_type T>
33+
template <safe_numbers::detail::integral_library_type T>
3434
BOOST_SAFE_NUMBERS_HOST_DEVICE constexpr auto to_chars(char* first, char* last, const T value, int base = 10)
3535
-> charconv::to_chars_result
3636
{
3737
using underlying_type = safe_numbers::detail::underlying_type_t<T>;
3838
return charconv::to_chars(first, last, static_cast<underlying_type>(value), base);
3939
}
4040

41+
template <safe_numbers::detail::float_library_type T>
42+
auto from_chars(const char* first, const char* last, T& value, chars_format fmt = chars_format::general)
43+
-> charconv::from_chars_result
44+
{
45+
using underlying_type = safe_numbers::detail::underlying_type_t<T>;
46+
47+
underlying_type result {};
48+
const auto r {charconv::from_chars(first, last, result, fmt)};
49+
value = T{result};
50+
51+
return r;
52+
}
53+
54+
template <safe_numbers::detail::float_library_type T>
55+
auto to_chars(char* first, char* last, const T value, chars_format fmt = chars_format::general)
56+
-> charconv::to_chars_result
57+
{
58+
using underlying_type = safe_numbers::detail::underlying_type_t<T>;
59+
return charconv::to_chars(first, last, static_cast<underlying_type>(value), fmt);
60+
}
61+
62+
template <safe_numbers::detail::float_library_type T>
63+
auto to_chars(char* first, char* last, const T value, chars_format fmt, int precision)
64+
-> charconv::to_chars_result
65+
{
66+
using underlying_type = safe_numbers::detail::underlying_type_t<T>;
67+
return charconv::to_chars(first, last, static_cast<underlying_type>(value), fmt, precision);
68+
}
69+
4170
} // namespace boost::charconv
4271

4372
#endif // BOOST_SAFE_NUMBERS_CHARCONV_HPP
Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
// Copyright 2026 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#ifndef BOOST_SAFE_NUMBERS_CMATH_HPP
6+
#define BOOST_SAFE_NUMBERS_CMATH_HPP
7+
8+
#include <boost/safe_numbers/floats.hpp>
9+
#include <boost/safe_numbers/signed_integers.hpp>
10+
#include <boost/safe_numbers/detail/type_traits.hpp>
11+
12+
#ifndef BOOST_SAFE_NUMBERS_BUILD_MODULE
13+
14+
#if (defined(BOOST_SAFE_NUMBERS_ENABLE_CUDA) && defined(__CUDACC__))
15+
#include <cuda/std/cmath>
16+
#else
17+
#include <cmath>
18+
#endif
19+
20+
#include <cstdint>
21+
#include <type_traits>
22+
23+
#endif // BOOST_SAFE_NUMBERS_BUILD_MODULE
24+
25+
// Namespace that actually provides the <cmath> functions. The wrappers delegate
26+
// to it with an explicit qualification so there is no ADL or self-recursion.
27+
#if (defined(BOOST_SAFE_NUMBERS_ENABLE_CUDA) && defined(__CUDACC__))
28+
# define BOOST_SAFE_NUMBERS_DETAIL_CMATH_NS cuda::std
29+
#else
30+
# define BOOST_SAFE_NUMBERS_DETAIL_CMATH_NS std
31+
#endif
32+
33+
namespace boost::safe_numbers {
34+
35+
#define BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(fn) \
36+
template <detail::non_bounded_float_library_type T> \
37+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto fn(const T x) -> T \
38+
{ \
39+
using underlying_type = detail::underlying_type_t<T>; \
40+
return T{detail::impl::check_cmath_result<underlying_type>( \
41+
BOOST_SAFE_NUMBERS_DETAIL_CMATH_NS::fn(static_cast<underlying_type>(x)), #fn)}; \
42+
}
43+
44+
#define BOOST_SAFE_NUMBERS_DETAIL_FLOAT_BINARY(fn) \
45+
template <detail::non_bounded_float_library_type T> \
46+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto fn(const T x, const T y) -> T \
47+
{ \
48+
using underlying_type = detail::underlying_type_t<T>; \
49+
return T{detail::impl::check_cmath_result<underlying_type>( \
50+
BOOST_SAFE_NUMBERS_DETAIL_CMATH_NS::fn(static_cast<underlying_type>(x), \
51+
static_cast<underlying_type>(y)), #fn)}; \
52+
}
53+
54+
// Trigonometric
55+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(sin)
56+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(cos)
57+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(tan)
58+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(asin)
59+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(acos)
60+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(atan)
61+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_BINARY(atan2)
62+
63+
// Hyperbolic
64+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(sinh)
65+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(cosh)
66+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(tanh)
67+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(asinh)
68+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(acosh)
69+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(atanh)
70+
71+
// Exponential and logarithmic
72+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(exp)
73+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(exp2)
74+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(expm1)
75+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(log)
76+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(log2)
77+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(log10)
78+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(log1p)
79+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(logb)
80+
81+
// Power and root
82+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(sqrt)
83+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(cbrt)
84+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_BINARY(pow)
85+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_BINARY(hypot)
86+
87+
// Error and gamma
88+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(erf)
89+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(erfc)
90+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(tgamma)
91+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(lgamma)
92+
93+
// Nearest integer (floating-point result)
94+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(floor)
95+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(ceil)
96+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(trunc)
97+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(round)
98+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(nearbyint)
99+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(rint)
100+
101+
// Absolute value
102+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(abs)
103+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY(fabs)
104+
105+
// Remainder, difference, min/max, sign manipulation
106+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_BINARY(fmod)
107+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_BINARY(remainder)
108+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_BINARY(fdim)
109+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_BINARY(fmin)
110+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_BINARY(fmax)
111+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_BINARY(copysign)
112+
BOOST_SAFE_NUMBERS_DETAIL_FLOAT_BINARY(nextafter)
113+
114+
// Fused multiply-add
115+
template <detail::non_bounded_float_library_type T>
116+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto fma(const T x, const T y, const T z) -> T
117+
{
118+
using underlying_type = detail::underlying_type_t<T>;
119+
return T{detail::impl::check_cmath_result<underlying_type>(
120+
BOOST_SAFE_NUMBERS_DETAIL_CMATH_NS::fma(static_cast<underlying_type>(x),
121+
static_cast<underlying_type>(y),
122+
static_cast<underlying_type>(z)), "fma")};
123+
}
124+
125+
// Scaling by a power of two (integer exponent input, floating-point result)
126+
template <detail::non_bounded_float_library_type T>
127+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto ldexp(const T x, const int exp) -> T
128+
{
129+
using underlying_type = detail::underlying_type_t<T>;
130+
return T{detail::impl::check_cmath_result<underlying_type>(
131+
BOOST_SAFE_NUMBERS_DETAIL_CMATH_NS::ldexp(static_cast<underlying_type>(x), exp), "ldexp")};
132+
}
133+
134+
template <detail::non_bounded_float_library_type T>
135+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto scalbn(const T x, const int exp) -> T
136+
{
137+
using underlying_type = detail::underlying_type_t<T>;
138+
return T{detail::impl::check_cmath_result<underlying_type>(
139+
BOOST_SAFE_NUMBERS_DETAIL_CMATH_NS::scalbn(static_cast<underlying_type>(x), exp), "scalbn")};
140+
}
141+
142+
template <detail::non_bounded_float_library_type T>
143+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto scalbln(const T x, const long exp) -> T
144+
{
145+
using underlying_type = detail::underlying_type_t<T>;
146+
return T{detail::impl::check_cmath_result<underlying_type>(
147+
BOOST_SAFE_NUMBERS_DETAIL_CMATH_NS::scalbln(static_cast<underlying_type>(x), exp), "scalbln")};
148+
}
149+
150+
// ------------------------------
151+
// Classification predicates
152+
// ------------------------------
153+
// These inspect a value and never throw, so they are the escape hatch for code
154+
// that needs to reason about infinities and NANs. They are genuinely constexpr
155+
// because they delegate to the library's own constexpr classification helpers.
156+
157+
template <detail::non_bounded_float_library_type T>
158+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto isnan(const T x) noexcept -> bool
159+
{
160+
return detail::impl::constexpr_isnan(static_cast<detail::underlying_type_t<T>>(x));
161+
}
162+
163+
template <detail::non_bounded_float_library_type T>
164+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto isinf(const T x) noexcept -> bool
165+
{
166+
return detail::impl::constexpr_isinf(static_cast<detail::underlying_type_t<T>>(x));
167+
}
168+
169+
template <detail::non_bounded_float_library_type T>
170+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto isfinite(const T x) noexcept -> bool
171+
{
172+
const auto v {static_cast<detail::underlying_type_t<T>>(x)};
173+
return !detail::impl::constexpr_isinf(v) && !detail::impl::constexpr_isnan(v);
174+
}
175+
176+
template <detail::non_bounded_float_library_type T>
177+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto isnormal(const T x) noexcept -> bool
178+
{
179+
return detail::impl::constexpr_isnormal(static_cast<detail::underlying_type_t<T>>(x));
180+
}
181+
182+
template <detail::non_bounded_float_library_type T>
183+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto signbit(const T x) noexcept -> bool
184+
{
185+
return detail::impl::constexpr_signbit(static_cast<detail::underlying_type_t<T>>(x));
186+
}
187+
188+
template <detail::non_bounded_float_library_type T>
189+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto fpclassify(const T x) noexcept -> int
190+
{
191+
return detail::impl::constexpr_fpclassify(static_cast<detail::underlying_type_t<T>>(x));
192+
}
193+
194+
// Non-signaling comparison predicates: ordered comparisons that return false
195+
// (rather than raising) when an operand is NAN.
196+
197+
template <detail::non_bounded_float_library_type T>
198+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto isunordered(const T x, const T y) noexcept -> bool
199+
{
200+
using underlying_type = detail::underlying_type_t<T>;
201+
return detail::impl::constexpr_isnan(static_cast<underlying_type>(x)) ||
202+
detail::impl::constexpr_isnan(static_cast<underlying_type>(y));
203+
}
204+
205+
template <detail::non_bounded_float_library_type T>
206+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto isgreater(const T x, const T y) noexcept -> bool
207+
{
208+
using underlying_type = detail::underlying_type_t<T>;
209+
return !isunordered(x, y) && static_cast<underlying_type>(x) > static_cast<underlying_type>(y);
210+
}
211+
212+
template <detail::non_bounded_float_library_type T>
213+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto isgreaterequal(const T x, const T y) noexcept -> bool
214+
{
215+
using underlying_type = detail::underlying_type_t<T>;
216+
return !isunordered(x, y) && static_cast<underlying_type>(x) >= static_cast<underlying_type>(y);
217+
}
218+
219+
template <detail::non_bounded_float_library_type T>
220+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto isless(const T x, const T y) noexcept -> bool
221+
{
222+
using underlying_type = detail::underlying_type_t<T>;
223+
return !isunordered(x, y) && static_cast<underlying_type>(x) < static_cast<underlying_type>(y);
224+
}
225+
226+
template <detail::non_bounded_float_library_type T>
227+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto islessequal(const T x, const T y) noexcept -> bool
228+
{
229+
using underlying_type = detail::underlying_type_t<T>;
230+
return !isunordered(x, y) && static_cast<underlying_type>(x) <= static_cast<underlying_type>(y);
231+
}
232+
233+
template <detail::non_bounded_float_library_type T>
234+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] constexpr auto islessgreater(const T x, const T y) noexcept -> bool
235+
{
236+
using underlying_type = detail::underlying_type_t<T>;
237+
const auto a {static_cast<underlying_type>(x)};
238+
const auto b {static_cast<underlying_type>(y)};
239+
return !isunordered(x, y) && (a < b || a > b);
240+
}
241+
242+
// ------------------------------
243+
// Integer-returning functions
244+
// ------------------------------
245+
// These return the safe integer type that matches the width of the std return
246+
// type. A non-finite input has an unspecified std result (and raises FE_INVALID),
247+
// so it is reported as a domain error instead. Not constexpr: the underlying std
248+
// functions are not constexpr in C++20/23.
249+
250+
template <detail::non_bounded_float_library_type T>
251+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] auto lround(const T x) -> i64
252+
{
253+
using underlying_type = detail::underlying_type_t<T>;
254+
const auto v {static_cast<underlying_type>(x)};
255+
if (detail::impl::constexpr_isnan(v) || detail::impl::constexpr_isinf(v))
256+
{
257+
detail::impl::throw_cmath_domain<underlying_type>("lround");
258+
}
259+
return i64{static_cast<std::int64_t>(BOOST_SAFE_NUMBERS_DETAIL_CMATH_NS::lround(v))};
260+
}
261+
262+
template <detail::non_bounded_float_library_type T>
263+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] auto llround(const T x) -> i64
264+
{
265+
using underlying_type = detail::underlying_type_t<T>;
266+
const auto v {static_cast<underlying_type>(x)};
267+
if (detail::impl::constexpr_isnan(v) || detail::impl::constexpr_isinf(v))
268+
{
269+
detail::impl::throw_cmath_domain<underlying_type>("llround");
270+
}
271+
return i64{static_cast<std::int64_t>(BOOST_SAFE_NUMBERS_DETAIL_CMATH_NS::llround(v))};
272+
}
273+
274+
template <detail::non_bounded_float_library_type T>
275+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] auto lrint(const T x) -> i64
276+
{
277+
using underlying_type = detail::underlying_type_t<T>;
278+
const auto v {static_cast<underlying_type>(x)};
279+
if (detail::impl::constexpr_isnan(v) || detail::impl::constexpr_isinf(v))
280+
{
281+
detail::impl::throw_cmath_domain<underlying_type>("lrint");
282+
}
283+
return i64{static_cast<std::int64_t>(BOOST_SAFE_NUMBERS_DETAIL_CMATH_NS::lrint(v))};
284+
}
285+
286+
template <detail::non_bounded_float_library_type T>
287+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] auto llrint(const T x) -> i64
288+
{
289+
using underlying_type = detail::underlying_type_t<T>;
290+
const auto v {static_cast<underlying_type>(x)};
291+
if (detail::impl::constexpr_isnan(v) || detail::impl::constexpr_isinf(v))
292+
{
293+
detail::impl::throw_cmath_domain<underlying_type>("llrint");
294+
}
295+
return i64{static_cast<std::int64_t>(BOOST_SAFE_NUMBERS_DETAIL_CMATH_NS::llrint(v))};
296+
}
297+
298+
template <detail::non_bounded_float_library_type T>
299+
BOOST_SAFE_NUMBERS_HOST_DEVICE [[nodiscard]] auto ilogb(const T x) -> i32
300+
{
301+
using underlying_type = detail::underlying_type_t<T>;
302+
const auto v {static_cast<underlying_type>(x)};
303+
const auto cls {detail::impl::constexpr_fpclassify(v)};
304+
if (cls == FP_NAN || cls == FP_INFINITE || cls == FP_ZERO)
305+
{
306+
detail::impl::throw_cmath_domain<underlying_type>("ilogb");
307+
}
308+
return i32{static_cast<std::int32_t>(BOOST_SAFE_NUMBERS_DETAIL_CMATH_NS::ilogb(v))};
309+
}
310+
311+
#undef BOOST_SAFE_NUMBERS_DETAIL_FLOAT_UNARY
312+
#undef BOOST_SAFE_NUMBERS_DETAIL_FLOAT_BINARY
313+
314+
} // namespace boost::safe_numbers
315+
316+
#undef BOOST_SAFE_NUMBERS_DETAIL_CMATH_NS
317+
318+
#endif // BOOST_SAFE_NUMBERS_CMATH_HPP

0 commit comments

Comments
 (0)