Skip to content

Commit 147704b

Browse files
committed
WIP: implementing nextafter for double-double
1 parent 58500ba commit 147704b

File tree

11 files changed

+222
-39
lines changed

11 files changed

+222
-39
lines changed

include/universal/native/ieee754_numeric.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace sw { namespace universal {
1616
typename = typename ::std::enable_if< ::std::is_floating_point<Real>::value, Real >::type
1717
>
1818
inline Real ulp(const Real& a) {
19-
return std::nextafter(a, a + a/2.0f) - a;
19+
return std::nextafter(a, Real(INFINITY)) - a;
2020
}
2121

2222
// check if the floating-point number is zero

include/universal/number/dd/dd_impl.hpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929

3030
namespace sw { namespace universal {
3131

32-
// fwd references to free functions used in to_digits()
32+
// fwd references to free functions
33+
dd operator-(const dd& lhs, const dd&);
3334
dd operator*(const dd& lhs, const dd&);
3435
std::ostream& operator<<(std::ostream&, const dd&);
3536
dd pown(const dd&, int);
@@ -865,6 +866,14 @@ inline std::string to_binary(const dd& number, bool bNibbleMarker = false) {
865866

866867
//////////////////////// math functions /////////////////////////////////
867868

869+
inline dd ulp(const dd& a) {
870+
double hi{ a.high() };
871+
double nlo = std::nextafter(a.low(), INFINITY);
872+
dd n(hi, nlo);
873+
874+
return n - a;
875+
}
876+
868877
inline dd abs(dd a) {
869878
double hi = a.high();
870879
double lo = a.low();

include/universal/number/dd/math/next.hpp

+7-16
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,16 @@ Return Value
2424
- And math_errhandling has MATH_ERRNO set: the global variable errno is set to ERANGE.
2525
- And math_errhandling has MATH_ERREXCEPT set: FE_OVERFLOW is raised.
2626
*/
27-
dd nextafter(dd x, dd target) {
28-
if (x == target) return target;
29-
if (target.isnan()) {
30-
if (x.isneg()) {
31-
--x;
32-
}
33-
else {
34-
++x;
35-
}
27+
dd nextafter(const dd& x, const dd& target) {
28+
double hi{ x.high() };
29+
double lo;
30+
if (x < target) {
31+
lo = std::nextafter(x.low(), +INFINITY);
3632
}
3733
else {
38-
if (x > target) {
39-
--x;
40-
}
41-
else {
42-
++x;
43-
}
34+
lo = std::nextafter(x.low(), -INFINITY);
4435
}
45-
return x;
36+
return dd(hi, lo);
4637
}
4738

4839
/* TODO

static/dd/api/api.cpp

+42-11
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ namespace sw {
6161
ostr << str << '\n';
6262
}
6363

64-
void construct_largest_doubledouble() {
64+
void construct_largest_double_double() {
6565
using Scalar = dd;
6666

6767
double firstLimb = std::numeric_limits<double>::max();
@@ -89,6 +89,26 @@ namespace sw {
8989
std::cout << c << '\n';
9090
}
9191

92+
template<typename Real,
93+
typename = typename ::std::enable_if< ::std::is_floating_point<Real>::value, Real >::type
94+
>
95+
Real emulateNextAfter(Real x, Real y) {
96+
if (x == y) return y;
97+
int direction = (x < y) ? 1 : -1;
98+
Real eps = std::numeric_limits<Real>::epsilon();
99+
return x + direction * eps;
100+
}
101+
102+
void ulp_progression(const std::string& tag, const dd& start) {
103+
std::cout << tag;
104+
for (dd from = start, to, delta;
105+
(delta = (to = nextafter(from, +INFINITY)) - from) < 10.0;
106+
from *= 10.0) {
107+
std::cout << "ulp(" << std::scientific << std::setprecision(0) << from
108+
<< ") gives " << to_binary(ulp(from)) << " : "
109+
<< std::fixed << std::setprecision(6) << ulp(from) << '\n';
110+
}
111+
}
92112
}
93113
}
94114

@@ -183,22 +203,22 @@ try {
183203
}
184204

185205
// report on the dynamic range of some standard configurations
186-
std::cout << "+--------- Dynamic range doubledouble configurations ---------+\n";
206+
std::cout << "+--------- Dynamic range double-double configurations ---------+\n";
187207
{
188208
dd a; // uninitialized
189209

190210
a.maxpos();
191-
std::cout << "maxpos doubledouble : " << to_binary(a) << " : " << a << '\n';
211+
std::cout << "maxpos double-double : " << to_binary(a) << " : " << a << '\n';
192212
a.setbits(0x0080); // positive min normal
193-
std::cout << "minnorm doubledouble : " << to_binary(a) << " : " << a << '\n';
213+
std::cout << "minnorm double-double : " << to_binary(a) << " : " << a << '\n';
194214
a.minpos();
195-
std::cout << "minpos doubledouble : " << to_binary(a) << " : " << a << '\n';
215+
std::cout << "minpos double-double : " << to_binary(a) << " : " << a << '\n';
196216
a.zero();
197217
std::cout << "zero : " << to_binary(a) << " : " << a << '\n';
198218
a.minneg();
199-
std::cout << "minneg doubledouble : " << to_binary(a) << " : " << a << '\n';
219+
std::cout << "minneg double-double : " << to_binary(a) << " : " << a << '\n';
200220
a.maxneg();
201-
std::cout << "maxneg doubledouble : " << to_binary(a) << " : " << a << '\n';
221+
std::cout << "maxneg double-double : " << to_binary(a) << " : " << a << '\n';
202222

203223
std::cout << "---\n";
204224
}
@@ -302,7 +322,7 @@ try {
302322
std::cout << dynamic_range<dd>() << std::endl;
303323
}
304324

305-
std::cout << "+--------- doubledouble subnormal behavior --------+\n";
325+
std::cout << "+--------- double-double subnormal behavior --------+\n";
306326
{
307327
constexpr double minpos = std::numeric_limits<double>::min();
308328
std::cout << to_binary(minpos) << " : " << minpos << '\n';
@@ -316,7 +336,7 @@ try {
316336
}
317337
}
318338

319-
std::cout << "+--------- special value properties doubledouble vs IEEE-754 --------+\n";
339+
std::cout << "+--------- special value properties double-double vs IEEE-754 --------+\n";
320340
{
321341
float fa;
322342
fa = NAN;
@@ -331,14 +351,25 @@ try {
331351

332352
dd a(fa);
333353
if ((a < 0.0f && a > 0.0f && a != 0.0f)) {
334-
std::cout << "doubledouble (dd) is incorrectly implemented\n";
354+
std::cout << "double-double (dd) is incorrectly implemented\n";
335355
++nrOfFailedTestCases;
336356
}
337357
else {
338-
std::cout << "dd NAN has no sign\n";
358+
std::cout << "double-double (dd) NAN has no sign\n";
339359
}
340360
}
341361

362+
std::cout << "---------- Unit in the Last Place --------+\n";
363+
{
364+
dd a{ 1.0 };
365+
366+
ulp_progression("\nULP progression for dd:\n", dd(10.0));
367+
368+
using float_type = ::std::enable_if< ::std::is_floating_point<float>::value, float >::type;
369+
using double_type = ::std::enable_if< ::std::is_floating_point<double>::value, double >::type;
370+
// using bla = ::std::enable_if< ::std::is_floating_point<int>::value, int >::type
371+
}
372+
342373
std::cout << "+--------- numeric_limits of double-double vs IEEE-754 --------+\n";
343374
{
344375
std::cout << "dd(INFINITY): " << dd(INFINITY) << "\n";

static/native/float/fractionviz.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// fractionviz.cpp : fraction bits visualization of native IEEE-754 types
22
//
3-
// Copyright (C) 2017-2023 Stillwater Supercomputing, Inc.
3+
// Copyright (C) 2017 Stillwater Supercomputing, Inc.
4+
// SPDX-License-Identifier: MIT
45
//
56
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
67
#include <universal/utility/directives.hpp>

static/native/float/ieee754.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// ieee754.cpp : native IEEE-754 operations
22
//
3-
// Copyright (C) 2017-2023 Stillwater Supercomputing, Inc.
3+
// Copyright (C) 2017 Stillwater Supercomputing, Inc.
4+
// SPDX-License-Identifier: MIT
45
//
56
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
67
#include <universal/utility/directives.hpp>

static/native/float/manipulators.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// manipulators.cpp : test suite runner for native floating-point manipulators
22
//
3-
// Copyright (C) 2017-2023 Stillwater Supercomputing, Inc.
3+
// Copyright (C) 2017 Stillwater Supercomputing, Inc.
4+
// SPDX-License-Identifier: MIT
45
//
56
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
67
#include <universal/utility/directives.hpp>

static/native/float/mathlib.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// mathlib.cpp : universal math library wrapper
22
//
3-
// Copyright (C) 2017-2023 Stillwater Supercomputing, Inc.
3+
// Copyright (C) 2017 Stillwater Supercomputing, Inc.
4+
// SPDX-License-Identifier: MIT
45
//
56
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
67
#include <universal/utility/directives.hpp>

static/native/float/nextafter.cpp

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// nextafter.cpp : exploration of the nextafter stdlib function to manipulate units in the last place
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+
#include <universal/utility/directives.hpp>
8+
#include <cmath>
9+
#include <concepts>
10+
#include <cfenv>
11+
#include <universal/verification/test_suite.hpp>
12+
13+
// Regression testing guards: typically set by the cmake configuration, but MANUAL_TESTING is an override
14+
#define MANUAL_TESTING 0
15+
// REGRESSION_LEVEL_OVERRIDE is set by the cmake file to drive a specific regression intensity
16+
// It is the responsibility of the regression test to organize the tests in a quartile progression.
17+
//#undef REGRESSION_LEVEL_OVERRIDE
18+
#ifndef REGRESSION_LEVEL_OVERRIDE
19+
#undef REGRESSION_LEVEL_1
20+
#undef REGRESSION_LEVEL_2
21+
#undef REGRESSION_LEVEL_3
22+
#undef REGRESSION_LEVEL_4
23+
#define REGRESSION_LEVEL_1 1
24+
#define REGRESSION_LEVEL_2 1
25+
#define REGRESSION_LEVEL_3 1
26+
#define REGRESSION_LEVEL_4 1
27+
#endif
28+
29+
int main()
30+
try {
31+
using namespace sw::universal;
32+
33+
std::string test_suite = "nextafter test";
34+
std::string test_tag = "nextafter";
35+
bool reportTestCases = false;
36+
int nrOfFailedTestCases = 0;
37+
38+
ReportTestSuiteHeader(test_suite, reportTestCases);
39+
40+
{
41+
float from = 0, to = std::nextafter(from, 1.f);
42+
std::cout << "The next representable float after " << std::setprecision(20) << from
43+
<< " is " << to
44+
<< std::hexfloat << " (" << to << ")\n" << std::defaultfloat;
45+
}
46+
47+
{
48+
float from = 1, to = std::nextafter(from, 2.f);
49+
std::cout << "The next representable float after " << from << " is " << to
50+
<< std::hexfloat << " (" << to << ")\n" << std::defaultfloat;
51+
}
52+
53+
{
54+
double from = std::nextafter(0.1, 0), to = 0.1;
55+
std::cout << "The number 0.1 lies between two valid doubles:\n"
56+
<< std::setprecision(56) << " " << from
57+
<< std::hexfloat << " (" << from << ')' << std::defaultfloat
58+
<< "\nand " << to << std::hexfloat << " (" << to << ")\n"
59+
<< std::defaultfloat << std::setprecision(20);
60+
}
61+
62+
{
63+
std::cout << "\nDifference between nextafter and nexttoward:\n";
64+
float from = 0.0f;
65+
long double dir = std::nextafter(from, 1.0L); // first subnormal long double
66+
float x = std::nextafter(from, dir); // first converts dir to float, giving 0
67+
std::cout << "With nextafter, next float after " << from << " is " << x << '\n';
68+
x = std::nexttoward(from, dir);
69+
std::cout << "With nexttoward, next float after " << from << " is " << x << '\n';
70+
}
71+
72+
std::cout << "\nSpecial values:\n";
73+
{
74+
// #pragma STDC FENV_ACCESS ON
75+
std::feclearexcept(FE_ALL_EXCEPT);
76+
double from4 = DBL_MAX, to4 = std::nextafter(from4, INFINITY);
77+
std::cout << "The next representable double after " << std::setprecision(6)
78+
<< from4 << std::hexfloat << " (" << from4 << ')'
79+
<< std::defaultfloat << " is " << to4
80+
<< std::hexfloat << " (" << to4 << ")\n" << std::defaultfloat;
81+
82+
if (std::fetestexcept(FE_OVERFLOW))
83+
std::cout << " raised FE_OVERFLOW\n";
84+
if (std::fetestexcept(FE_INEXACT))
85+
std::cout << " raised FE_INEXACT\n";
86+
} // end FENV_ACCESS block
87+
88+
{
89+
float from = 0.0, to = std::nextafter(from, -0.0);
90+
std::cout << "std::nextafter(+0.0, -0.0) gives " << std::fixed << to << '\n';
91+
}
92+
93+
auto precision_loss_demo = []<std::floating_point Fp>(const auto rem, const Fp start)
94+
{
95+
std::cout << rem;
96+
for (Fp from = start, to, delta;
97+
(delta = (to = std::nextafter(from, +INFINITY)) - from) < Fp(10.0);
98+
from *= Fp(10.0)) {
99+
std::cout << "nextafter(" << std::scientific << std::setprecision(0) << from
100+
<< ", INF) gives " << std::fixed << std::setprecision(6) << to
101+
<< "; delta = " << delta << '\n';
102+
}
103+
};
104+
105+
precision_loss_demo("\nPrecision loss demo for float:\n", 10.0f);
106+
precision_loss_demo("\nPrecision loss demo for double:\n", 10.0e9);
107+
108+
#if LONG_DOUBLE_SUPPORT
109+
precision_loss_demo("\nPrecision loss demo for long double:\n", 10.0e17L);
110+
111+
constexpr long double denorm_min = std::numeric_limits<long double>::denorm_min();
112+
std::cout << "smallest long double: " << to_binary(denorm_min) << " : " << denorm_min << '\n';
113+
#endif
114+
115+
116+
auto ulp_progression = []<std::floating_point Fp>(const auto rem, const Fp start)
117+
{
118+
std::cout << rem;
119+
for (Fp from = start, to, delta;
120+
(delta = (to = std::nextafter(from, +INFINITY)) - from) < Fp(10.0);
121+
from *= Fp(10.0)) {
122+
std::cout << "ulp(" << std::scientific << std::setprecision(0) << from
123+
<< ") gives " << to_binary(ulp(from)) << " : "
124+
<< std::fixed << std::setprecision(6) << ulp(from) << '\n';
125+
}
126+
};
127+
128+
ulp_progression("\nULP progression for float:\n", 10.0f);
129+
ulp_progression("\nULP progression for double:\n", 10.0e9);
130+
131+
std::cout << '\n';
132+
ReportTestSuiteResults(test_suite, nrOfFailedTestCases);
133+
return (nrOfFailedTestCases > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
134+
}
135+
catch (char const* msg) {
136+
std::cerr << msg << '\n';
137+
return EXIT_FAILURE;
138+
}
139+
catch (const std::runtime_error& err) {
140+
std::cerr << "Uncaught runtime exception: " << err.what() << std::endl;
141+
return EXIT_FAILURE;
142+
}
143+
catch (...) {
144+
std::cerr << "Caught unknown exception" << '\n';
145+
return EXIT_FAILURE;
146+
}

static/native/float/representable.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// representable.cpp : check if a ratio is representable without error as a floating-point value
22
//
3-
// Copyright (C) 2017-2023 Stillwater Supercomputing, Inc.
3+
// Copyright (C) 2017 Stillwater Supercomputing, Inc.
4+
// SPDX-License-Identifier: MIT
45
//
56
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
67
#include <universal/utility/directives.hpp>

0 commit comments

Comments
 (0)