Skip to content

Commit 1138d18

Browse files
committed
Add float next_toward() method
1 parent 72b5116 commit 1138d18

File tree

4 files changed

+48
-2
lines changed

4 files changed

+48
-2
lines changed

subspace/num/__private/float_methods.inc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,20 @@ sus_pure inline _self mul_add(_self a, _self b) const& noexcept {
489489
return static_cast<_primitive>(
490490
::fma(primitive_value, a.primitive_value, b.primitive_value));
491491
}
492+
493+
/// Returns the next representable value of the float type after `self` in the
494+
/// direction of `toward`. If `self == toward`, `toward` is returned. If either
495+
/// `self` or `toward` is NAN, NAN is returned.
496+
///
497+
/// This is implemented by the
498+
/// [cmath](https://en.cppreference.com/w/c/numeric/math/nextafter) library, see
499+
/// the documentation for details on errors.
500+
//
501+
// TODO: constexpr in C++23.
502+
sus_pure inline _self next_toward(_self toward) const& noexcept {
503+
return __private::next_toward(primitive_value, toward.primitive_value);
504+
}
505+
492506
/// Raises a number to a floating point power.
493507
sus_pure inline _self powf(_self n) const& noexcept {
494508
// MSVC pow(float) is returning a double for some reason.

subspace/num/__private/intrinsics.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,20 @@
1414

1515
#pragma once
1616

17-
#include <math.h> // TODO: Remove this and define nans and truncf ourselves.
1817
#include <stddef.h>
1918
#include <stdint.h>
2019

2120
#include <bit>
21+
#include <cmath>
2222
#include <type_traits>
2323

2424
#if _MSC_VER
2525
#include <intrin.h>
2626
#endif
2727

2828
#include "subspace/assertions/unreachable.h"
29-
#include "subspace/macros/inline.h"
3029
#include "subspace/macros/builtin.h"
30+
#include "subspace/macros/inline.h"
3131
#include "subspace/macros/pure.h"
3232
#include "subspace/marker/unsafe.h"
3333
#include "subspace/mem/size_of.h"
@@ -1511,4 +1511,14 @@ sus_pure_const constexpr inline T float_clamp(marker::UnsafeFnMarker, T x,
15111511
return x;
15121512
}
15131513

1514+
// TODO: constexpr in C++23.
1515+
template <class T>
1516+
requires(std::is_floating_point_v<T> && ::sus::mem::size_of<T>() <= 8)
1517+
sus_pure_const inline T next_toward(T from, T to) {
1518+
if constexpr (::sus::mem::size_of<T>() == 4u)
1519+
return std::nexttowardf(from, to);
1520+
else
1521+
return std::nexttoward(from, to);
1522+
}
1523+
15141524
} // namespace sus::num::__private

subspace/num/f32_unittest.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,4 +1106,15 @@ TEST(f32, fmt) {
11061106
EXPECT_EQ(fmt::format("{:.4f}", 1234.567_f32), "1234.5670");
11071107
}
11081108

1109+
TEST(f32, NextToward) {
1110+
EXPECT_EQ((0_f32).next_toward(f32::NAN).is_nan(), true);
1111+
EXPECT_EQ(f32::NAN.next_toward(0_f32).is_nan(), true);
1112+
EXPECT_EQ((1_f32).next_toward(f32::INFINITY) - 1_f32,
1113+
f32(std::numeric_limits<float>::epsilon()));
1114+
EXPECT_GT((0_f32).next_toward(f32::INFINITY), 0_f32);
1115+
EXPECT_GT((0_f32).next_toward(1_f32), 0_f32);
1116+
EXPECT_LT((0_f32).next_toward(f32::NEG_INFINITY), 0_f32);
1117+
EXPECT_LT((0_f32).next_toward(-1_f32), 0_f32);
1118+
}
1119+
11091120
} // namespace

subspace/num/f64_unittest.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,4 +1082,15 @@ TEST(f64, fmt) {
10821082
EXPECT_EQ(fmt::format("{:.4f}", 1234890.567_f64), "1234890.5670");
10831083
}
10841084

1085+
TEST(f64, NextToward) {
1086+
EXPECT_EQ((0_f64).next_toward(f64::NAN).is_nan(), true);
1087+
EXPECT_EQ(f64::NAN.next_toward(0_f64).is_nan(), true);
1088+
EXPECT_EQ((1_f64).next_toward(f64::INFINITY) - 1_f64,
1089+
f64(std::numeric_limits<double>::epsilon()));
1090+
EXPECT_GT((0_f64).next_toward(f64::INFINITY), 0_f64);
1091+
EXPECT_GT((0_f64).next_toward(1_f64), 0_f64);
1092+
EXPECT_LT((0_f64).next_toward(f64::NEG_INFINITY), 0_f64);
1093+
EXPECT_LT((0_f64).next_toward(-1_f64), 0_f64);
1094+
}
1095+
10851096
} // namespace

0 commit comments

Comments
 (0)