Skip to content

Commit

Permalink
Merge pull request #5510 from pentacular/wasm
Browse files Browse the repository at this point in the history
Round-to-nearest support for intervals
  • Loading branch information
lrineau committed Apr 6, 2021
2 parents de704d8 + c26cb00 commit d46dcec
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 22 deletions.
78 changes: 63 additions & 15 deletions Number_types/include/CGAL/FPU.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,11 @@ extern "C" {

// Only define CGAL_USE_SSE2 for 64 bits where malloc has a suitable
// alignment, 32 bits is too dangerous.
#if defined(CGAL_HAS_SSE2) && (defined(__x86_64__) || defined(_M_X64))
#if defined CGAL_HAS_SSE2 && \
(defined __x86_64__ || defined _M_X64) && \
!defined CGAL_ALWAYS_ROUND_TO_NEAREST
# define CGAL_USE_SSE2 1
#endif

#ifdef CGAL_CFG_DENORMALS_COMPILE_BUG
double& get_static_minimin(); // Defined in Interval_arithmetic_impl.h
#endif
Expand Down Expand Up @@ -347,18 +348,27 @@ inline double IA_bug_sqrt(double d)
// With GCC, we can do slightly better : test with __builtin_constant_p()
// that both arguments are constant before stopping one of them.
// Use inline functions instead ?
#define CGAL_IA_ADD(a,b) CGAL_IA_FORCE_TO_DOUBLE((a)+CGAL_IA_STOP_CPROP(b))
#define CGAL_IA_SUB(a,b) CGAL_IA_FORCE_TO_DOUBLE(CGAL_IA_STOP_CPROP(a)-(b))
#define CGAL_IA_MUL(a,b) CGAL_IA_FORCE_TO_DOUBLE(CGAL_IA_STOP_CPROP(a)*CGAL_IA_STOP_CPROP(b))
#define CGAL_IA_DIV(a,b) CGAL_IA_FORCE_TO_DOUBLE(CGAL_IA_STOP_CPROP(a)/CGAL_IA_STOP_CPROP(b))
inline double IA_up(double d)
{
#ifdef CGAL_ALWAYS_ROUND_TO_NEAREST
// In round-to-nearest mode we find the successor instead.
// This preserves the interval invariants, but is more
// expensive and conservative.
return nextafter(d, std::numeric_limits<double>::infinity());
#else
// In round-upward mode we can rely on the hardware
// to do the job.
return CGAL_IA_FORCE_TO_DOUBLE(d);
#endif
}
#define CGAL_IA_ADD(a,b) IA_up((a)+CGAL_IA_STOP_CPROP(b))
#define CGAL_IA_SUB(a,b) IA_up(CGAL_IA_STOP_CPROP(a)-(b))
#define CGAL_IA_MUL(a,b) IA_up(CGAL_IA_STOP_CPROP(a)*CGAL_IA_STOP_CPROP(b))
#define CGAL_IA_DIV(a,b) IA_up(CGAL_IA_STOP_CPROP(a)/CGAL_IA_STOP_CPROP(b))
inline double CGAL_IA_SQUARE(double a){
double b = CGAL_IA_STOP_CPROP(a); // only once
return CGAL_IA_FORCE_TO_DOUBLE(b*b);
return IA_up(b*b);
}
#define CGAL_IA_SQRT(a) \
CGAL_IA_FORCE_TO_DOUBLE(CGAL_BUG_SQRT(CGAL_IA_STOP_CPROP(a)))


#if defined CGAL_SAFE_SSE2

#define CGAL_IA_SETFPCW(CW) _MM_SET_ROUNDING_MODE(CW)
Expand Down Expand Up @@ -476,9 +486,15 @@ inline
FPU_CW_t
FPU_get_cw (void)
{
#ifdef CGAL_ALWAYS_ROUND_TO_NEAREST
CGAL_assertion_code(FPU_CW_t cw; CGAL_IA_GETFPCW(cw);)
CGAL_assertion(cw == CGAL_FE_TONEAREST);
return CGAL_FE_TONEAREST;
#else
FPU_CW_t cw;
CGAL_IA_GETFPCW(cw);
return cw;
#endif
}

// User interface (cont):
Expand All @@ -487,28 +503,43 @@ inline
void
FPU_set_cw (FPU_CW_t cw)
{
#ifdef CGAL_ALWAYS_ROUND_TO_NEAREST
CGAL_assertion(cw == CGAL_FE_TONEAREST);
#else
CGAL_IA_SETFPCW(cw);
#endif
}

inline
FPU_CW_t
FPU_get_and_set_cw (FPU_CW_t cw)
{
#ifdef CGAL_ALWAYS_ROUND_TO_NEAREST
CGAL_assertion(cw == CGAL_FE_TONEAREST);
return CGAL_FE_TONEAREST;
#else
FPU_CW_t old = FPU_get_cw();
FPU_set_cw(cw);
return old;
#endif
}


// A class whose constructor sets the FPU mode to +inf, saves a backup of it,
// and whose destructor resets it back to the saved state.

#ifdef CGAL_ALWAYS_ROUND_TO_NEAREST
#define CGAL_FE_PROTECTED CGAL_FE_TONEAREST
#else
#define CGAL_FE_PROTECTED CGAL_FE_UPWARD
#endif

template <bool Protected = true> struct Protect_FPU_rounding;

template <>
struct Protect_FPU_rounding<true>
{
Protect_FPU_rounding(FPU_CW_t r = CGAL_FE_UPWARD)
Protect_FPU_rounding(FPU_CW_t r = CGAL_FE_PROTECTED)
: backup( FPU_get_and_set_cw(r) ) {}

~Protect_FPU_rounding()
Expand All @@ -524,7 +555,7 @@ template <>
struct Protect_FPU_rounding<false>
{
Protect_FPU_rounding() {}
Protect_FPU_rounding(FPU_CW_t /*= CGAL_FE_UPWARD*/) {}
Protect_FPU_rounding(FPU_CW_t /*= CGAL_FE_PROTECTED */) {}
};


Expand All @@ -538,13 +569,13 @@ struct Checked_protect_FPU_rounding
{
Checked_protect_FPU_rounding()
{
CGAL_expensive_assertion(FPU_get_cw() == CGAL_FE_UPWARD);
CGAL_expensive_assertion(FPU_get_cw() == CGAL_FE_PROTECTED);
}

Checked_protect_FPU_rounding(FPU_CW_t r)
: Protect_FPU_rounding<Protected>(r)
{
CGAL_expensive_assertion(FPU_get_cw() == CGAL_FE_UPWARD);
CGAL_expensive_assertion(FPU_get_cw() == CGAL_FE_PROTECTED);
}
};

Expand All @@ -555,6 +586,8 @@ struct Checked_protect_FPU_rounding
// Its destructor restores the FPU state as it was previously.
// Note that this affects "long double" as well, and other potential side effects.
// And note that it does not (cannot) "fix" the same problem for the exponent.
//
// (How should this interact with ALWAYS_ROUND_TO_NEAREST?)

struct Set_ieee_double_precision
#ifdef CGAL_FPU_HAS_EXCESS_PRECISION
Expand All @@ -579,6 +612,21 @@ inline void force_ieee_double_precision()
#endif
}

inline double IA_sqrt_up(double a) {
return IA_up(CGAL_BUG_SQRT(CGAL_IA_STOP_CPROP(a)));
}

inline double IA_sqrt_toward_zero(double d) {
#ifdef CGAL_ALWAYS_ROUND_TO_NEAREST
return (d > 0.0) ? nextafter(std::sqrt(d), 0.) : 0.0;
#else
FPU_set_cw(CGAL_FE_DOWNWARD);
double i = (d > 0.0) ? CGAL_IA_FORCE_TO_DOUBLE(CGAL_BUG_SQRT(CGAL_IA_STOP_CPROP(d))) : 0.0;
FPU_set_cw(CGAL_FE_UPWARD);
return i;
#endif
}

} //namespace CGAL

#ifdef CGAL_HEADER_ONLY
Expand Down
10 changes: 4 additions & 6 deletions Number_types/include/CGAL/Interval_nt.h
Original file line number Diff line number Diff line change
Expand Up @@ -1141,17 +1141,15 @@ namespace INTERN_INTERVAL_NT {
// it helps significantly, it might even hurt by introducing a
// dependency.
}
#else
#else // no __AVX512F__
// TODO: Alternative for computing CGAL_IA_SQRT_DOWN(d.inf()) exactly
// without changing the rounding mode:
// - compute x = CGAL_IA_SQRT(d.inf())
// - compute y = CGAL_IA_SQUARE(x)
// - if y==d.inf() use x, else use -CGAL_IA_SUB(CGAL_IA_MIN_DOUBLE,x)
FPU_set_cw(CGAL_FE_DOWNWARD);
double i = (d.inf() > 0.0) ? CGAL_IA_SQRT(d.inf()) : 0.0;
FPU_set_cw(CGAL_FE_UPWARD);
#endif
return Interval_nt<Protected>(i, CGAL_IA_SQRT(d.sup()));
double i = IA_sqrt_toward_zero(d.inf());
#endif // no __AVX512F__
return Interval_nt<Protected>(i, IA_sqrt_up(d.sup()));
}

template <bool Protected>
Expand Down
1 change: 1 addition & 0 deletions Number_types/test/Number_types/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ create_single_source_cgal_program("Gmpz.cpp")
create_single_source_cgal_program("Gmpzf_new.cpp")
create_single_source_cgal_program("int.cpp")
create_single_source_cgal_program("Interval_nt.cpp")
create_single_source_cgal_program("Interval_nt_nearest.cpp")
create_single_source_cgal_program("Interval_nt_new.cpp")
create_single_source_cgal_program("ioformat.cpp")
create_single_source_cgal_program("known_bit_size_integers.cpp")
Expand Down
40 changes: 39 additions & 1 deletion Number_types/test/Number_types/Interval_nt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,18 @@ bool spiral_test()
break;
};

#ifdef CGAL_ALWAYS_ROUND_TO_NEAREST
return i == 365;
#else
return i == 396;
#endif
}

// Tests for constant propagation through intervals.
// This must not be performed otherwise rounding modes are ignored.
// Non-inlined operators usually stop cprop (*, /, sqrt).
// On the other hand, if we always round to nearest, then constant propagation
// is desirable.
// Note: Non-inlined operators usually stop cprop (*, /, sqrt).
template < typename IA_nt >
bool cprop_test()
{
Expand Down Expand Up @@ -92,12 +98,31 @@ bool square_root_test()
a = b;
};
a -= 1.0;
#ifdef CGAL_ALWAYS_ROUND_TO_NEAREST
DEBUG (
std::cout << "i = " << i << std::endl;
std::cout << "sup : " << a.sup() << std::endl;
std::cout << "inf : " << a.inf() << std::endl;
) // DEBUG
if (i != 54) {
return false;
}
// When we round to nearest it doesn't quite converge.
if (a.sup() > 3/(double(1<<30)*(1<<22))) {
return false;
}
if (-3/(double(1<<30)*(1<<22)) > a.inf()) {
return false;
}
return true;
#else
DEBUG (
std::cout << "i = " << i << std::endl;
std::cout << "sup = -inf : " << (a.sup() == -a.inf()) << std::endl;
std::cout << "width ok ? : " << (-a.inf() == 1/(double(1<<30)*(1<<22))) << std::endl;
) // DEBUG
return i==54 && a.sup() == - a.inf() && a.sup() == 1/(double(1<<30)*(1<<22));
#endif
}


Expand Down Expand Up @@ -164,9 +189,15 @@ bool underflow_test()
for (i=0; i<20; i++) b = b * b;
for (i=0; i<20; i++) c = CGAL_NTS square(c);

#ifdef CGAL_ALWAYS_ROUND_TO_NEAREST
return a.is_same(IA_nt(-CGAL_IA_MIN_DOUBLE, CGAL_IA_MIN_DOUBLE))
&& b.is_same(IA_nt::smallest())
&& c.is_same(IA_nt(0, CGAL_IA_MIN_DOUBLE));
#else
return a.is_same(IA_nt(0, CGAL_IA_MIN_DOUBLE))
&& b.is_same(IA_nt::smallest())
&& c.is_same(IA_nt(0, CGAL_IA_MIN_DOUBLE));
#endif
}


Expand Down Expand Up @@ -432,10 +463,17 @@ bool test ()

int main()
{
#ifdef CGAL_ALWAYS_ROUND_TO_NEAREST
std::cout << "Stress-testing the class Interval_nt<> always rounding to nearest.\n";
bool ok = test<CGAL::Interval_nt<> >();
std::cout << "\nStress-testing the class Interval_nt_advanced always rounding to nearest.\n";
ok &= test<CGAL::Interval_nt_advanced>();
#else
std::cout << "Stress-testing the class Interval_nt<>.\n";
bool ok = test<CGAL::Interval_nt<> >();
std::cout << "\nStress-testing the class Interval_nt_advanced.\n";
ok &= test<CGAL::Interval_nt_advanced>();
#endif

return !ok;
}
4 changes: 4 additions & 0 deletions Number_types/test/Number_types/Interval_nt_nearest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#define CGAL_ALWAYS_ROUND_TO_NEAREST
#define CGAL_DEBUG
#define CGAL_CHECK_EXPENSIVE
#include "Interval_nt.cpp"
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ create_single_source_cgal_program("triangulate_faces_hole_filling_all_search_tes
create_single_source_cgal_program("test_pmp_remove_border_edge.cpp")
create_single_source_cgal_program("test_pmp_distance.cpp")
create_single_source_cgal_program("test_corefinement_and_constraints.cpp")
create_single_source_cgal_program("test_corefinement_and_constraints_nearest.cpp")
create_single_source_cgal_program("test_corefinement_bool_op.cpp")
create_single_source_cgal_program("test_corefine.cpp")
create_single_source_cgal_program("test_coref_epic_points_identity.cpp")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#define CGAL_ALWAYS_ROUND_TO_NEAREST
#include "test_corefinement_and_constraints.cpp"

0 comments on commit d46dcec

Please sign in to comment.