From 45dfcf788ad4d6d03c3e0ccb92cc35bf054abf36 Mon Sep 17 00:00:00 2001 From: Simon Praetorius Date: Tue, 1 Jan 2019 21:35:38 +0100 Subject: [PATCH] added implementation for quadmath, concurrentcache and strtonumber --- cmake/modules/AddQuadMathFlags.cmake | 28 ++ cmake/modules/CMakeLists.txt | 3 + cmake/modules/FindQuadMath.cmake | 59 ++++ dune/common/CMakeLists.txt | 8 +- dune/common/concurrentcache.hh | 235 +++++++++++++ dune/common/quadmath.hh | 441 ++++++++++++++++++++++++ dune/common/strtonumber.hh | 176 ++++++++++ dune/common/test/CMakeLists.txt | 11 +- dune/common/test/charconvtest.cc | 2 +- dune/common/test/concurrentcachetest.cc | 98 ++++++ dune/common/test/quadmathtest.cc | 141 ++++++++ dune/common/test/strtonumbertest.cc | 233 +++++++++++++ 12 files changed, 1432 insertions(+), 3 deletions(-) create mode 100644 cmake/modules/AddQuadMathFlags.cmake create mode 100644 cmake/modules/FindQuadMath.cmake create mode 100644 dune/common/concurrentcache.hh create mode 100644 dune/common/quadmath.hh create mode 100644 dune/common/strtonumber.hh create mode 100644 dune/common/test/concurrentcachetest.cc create mode 100644 dune/common/test/quadmathtest.cc create mode 100644 dune/common/test/strtonumbertest.cc diff --git a/cmake/modules/AddQuadMathFlags.cmake b/cmake/modules/AddQuadMathFlags.cmake new file mode 100644 index 0000000..cae9bbf --- /dev/null +++ b/cmake/modules/AddQuadMathFlags.cmake @@ -0,0 +1,28 @@ +# Defines the functions to use QuadMath +# +# .. cmake_function:: add_dune_quadmath_flags +# +# .. cmake_param:: targets +# :positional: +# :single: +# :required: +# +# A list of targets to use QuadMath with. +# + + +function(add_dune_quadmath_flags _targets) + if(QUADMATH_FOUND) + foreach(_target ${_targets}) + target_link_libraries(${_target} "quadmath") + set_property(TARGET ${_target} + APPEND_STRING + PROPERTY COMPILE_FLAGS "-DENABLE_QUADMATH=1 -D_GLIBCXX_USE_FLOAT128=1 ") + if(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU) + set_property(TARGET ${_target} + APPEND_STRING + PROPERTY COMPILE_FLAGS "-fext-numeric-literals ") + endif() + endforeach(_target ${_targets}) + endif(QUADMATH_FOUND) +endfunction(add_dune_quadmath_flags) diff --git a/cmake/modules/CMakeLists.txt b/cmake/modules/CMakeLists.txt index 62f2733..a2bfff0 100644 --- a/cmake/modules/CMakeLists.txt +++ b/cmake/modules/CMakeLists.txt @@ -1,3 +1,6 @@ set(modules "DuneCommonExtensionsMacros.cmake") +find_package(QuadMath) +include(AddQuadMathFlags) + install(FILES ${modules} DESTINATION ${DUNE_INSTALL_MODULEDIR}) diff --git a/cmake/modules/FindQuadMath.cmake b/cmake/modules/FindQuadMath.cmake new file mode 100644 index 0000000..ff70b33 --- /dev/null +++ b/cmake/modules/FindQuadMath.cmake @@ -0,0 +1,59 @@ +# .. cmake_module:: +# +# Find the GCC Quad-Precision library +# +# Sets the following variables: +# +# :code:`QUADMATH_FOUND` +# True if the Quad-Precision library was found. +# +# + + +# search for the header quadmath.h +include(CheckIncludeFile) +check_include_file(quadmath.h QUADMATH_HEADER) + +include(CheckCSourceCompiles) +include(CMakePushCheckState) + +cmake_push_check_state() # Save variables +set(CMAKE_REQUIRED_LIBRARIES quadmath) +check_c_source_compiles(" +#include + +int main () +{ + __float128 r = 1.0q; + r = strtoflt128(\"1.2345678\", NULL); + return 0; +}" QUADMATH_COMPILES) +cmake_pop_check_state() + + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + "QuadMath" + DEFAULT_MSG + QUADMATH_HEADER + QUADMATH_COMPILES +) + +# text for feature summary +set_package_properties("QuadMath" PROPERTIES + DESCRIPTION "GCC Quad-Precision library") + +# set HAVE_QUADMATH for config.h +set(HAVE_QUADMATH ${QUADMATH_FOUND}) + +# -fext-numeric-literals is a GCC extension not available in other compilers like clang +if(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU) + set(_QUADMATH_EXT_NUMERIC_LITERALS "-fext-numeric-literals") +endif() + +# register all QuadMath related flags +if(HAVE_QUADMATH) + dune_register_package_flags(COMPILE_DEFINITIONS "ENABLE_QUADMATH=1" "_GLIBCXX_USE_FLOAT128=1" + COMPILE_OPTIONS ${_QUADMATH_EXT_NUMERIC_LITERALS} + LIBRARIES "quadmath") +endif() diff --git a/dune/common/CMakeLists.txt b/dune/common/CMakeLists.txt index 21715c6..fa7a39f 100644 --- a/dune/common/CMakeLists.txt +++ b/dune/common/CMakeLists.txt @@ -1,2 +1,8 @@ add_subdirectory(std) -add_subdirectory(test) \ No newline at end of file +add_subdirectory(test) + +install(FILES + concurrentcache.hh + quadmath.hh + strtonumber.hh +DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/common) \ No newline at end of file diff --git a/dune/common/concurrentcache.hh b/dune/common/concurrentcache.hh new file mode 100644 index 0000000..d8095fd --- /dev/null +++ b/dune/common/concurrentcache.hh @@ -0,0 +1,235 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_CONCURRENT_CACHE_HH +#define DUNE_COMMON_CONCURRENT_CACHE_HH + +#include +#include +#include +#include +#include + +#include +#include + +namespace Dune +{ + /// Store cache in instance. + template + struct ConsecutivePolicy; + + /// Store cache thread local, requires no locking. + template + struct ThreadLocalPolicy; + + /// Stores cache global static, requires locking on write access. + template + struct StaticLockedPolicy; + + + /// \brief The class template ConcurrentCache describes an associative static container that allows the + /// concurrent access to the stored data. + /** + * Cache data of arbitray type that needs initialization on the first access. The data is thereby + * initialized thread-wise or globally only once, and guarantees that you always get initialized data. + * + * \tparam Key The type of key to access the data. + * \tparam Data The type of the data stored in the cache. The behaviur is undefined if Data is not + * the same type as Container::mapped_type. + * \tparam Policy A policy class template implementing the method `get_or_init()`. Three implementations + * are provided: \ref ConsecutivePolicy, \ref ThreadLocalPolicy and \ref StaticLockedPolicy. + * By default, if no policy class template is specified, the `ThreadLocalPolicy` is used. + * \see ConcurrentCachePolicy + * \tparam Container The type of the underlying associative container to use to store the data. The + * container must satisfy the requirements of AssociativeContainer. The standard + * containers `std::map` and `std::unordered_map` satisfie this requirement. By default, + * if not container class is specified, the standard container `std::unordered_map` + * is used. Note, an unordered_map requires the key to be hashable. + * + * The `Policy` class template is a template parametrizable with the container type, that provides a static `get_or_init()` + * method that is called with the key, and a functor for creation of new data elements. + **/ + template class Policy = ThreadLocalPolicy, + class Container = std::unordered_map> + class ConcurrentCache; + + +#ifdef DOXYGEN + /// \brief The class template ConcurrentCachePolicy describes a concrete policies for the use in \ref ConcurrentCache. + /** + * Provide a static cache and a `get_or_init()` static method that extracts the data from the cache if it exists or + * creates a new extry by using an initialization functor. + * + * Realizations of this template are \ref ConsecutivePolicy, \ref ThreadLocalPolicy and \ref StaticLockedPolicy. + * + * \tparam Container The Type of the associative container key->data to store the cached data. + **/ + template + class ConcurrentCachePolicy; +#endif + + // implementation of the consecutive policy. Data is stored in instance variable. + template + struct ConsecutivePolicy + { + using key_type = typename Container::key_type; + using data_type = typename Container::mapped_type; + using container_type = Container; + + template + data_type const& get_or_init(key_type const& key, F&& f, Args&&... args) const + { + return impl(std::is_default_constructible{}, + key, std::forward(f), std::forward(args)...); + } + + private: + // data_type is default_constructible + template + data_type const& impl(std::true_type, key_type const& key, F&& f, Args&&... args) const + { + data_type empty; + auto it = cachedData_.emplace(key, std::move(empty)); + if (it.second) { + data_type data = f(key, std::forward(args)...); + it.first->second = std::move(data); + } + return it.first->second; + } + + // data_type is not default_constructible + template + data_type const& impl(std::false_type, key_type const& key, F&& f, Args&&... args) const + { + auto it = cachedData_.find(key); + if (it != cachedData_.end()) + return it->second; + else { + data_type data = f(key, std::forward(args)...); + auto it = cachedData_.emplace(key, std::move(data)); + return it.first->second; + } + } + + mutable container_type cachedData_; + }; + + // implementation of the ThreadLocal policy. Data is stored in thread_local variable. + template + struct ThreadLocalPolicy + { + using key_type = typename Container::key_type; + using data_type = typename Container::mapped_type; + using container_type = Container; + + template + static data_type const& get_or_init(key_type const& key, F&& f, Args&&... args) + { + return impl(std::is_default_constructible{}, + key, std::forward(f), std::forward(args)...); + } + + private: + // data_type is default_constructible + template + static data_type const& impl(std::true_type, key_type const& key, F&& f, Args&&... args) + { + // Container to store the cached values + thread_local container_type cached_data; + + data_type empty; + auto it = cached_data.emplace(key, std::move(empty)); + if (it.second) { + data_type data = f(key, std::forward(args)...); + it.first->second = std::move(data); + } + return it.first->second; + } + + // data_type is not default_constructible + template + static data_type const& impl(std::false_type, key_type const& key, F&& f, Args&&... args) + { + // Container to store the cached values + thread_local container_type cached_data; + + auto it = cached_data.find(key); + if (it != cached_data.end()) + return it->second; + else { + data_type data = f(key, std::forward(args)...); + auto it = cached_data.emplace(key, std::move(data)); + return it.first->second; + } + } + }; + + + // implementation of the Shared policy. Data is stored in static variable. + template + struct StaticLockedPolicy + { + using key_type = typename Container::key_type; + using data_type = typename Container::mapped_type; + using container_type = Container; + + template + static data_type const& get_or_init(key_type const& key, F&& f, Args&&... args) + { + // Container to store the cached values + static container_type cached_data; + + // mutex used to access the data in the container, necessary since + // access emplace is read-write. + using mutex_type = std::shared_timed_mutex; + static mutex_type access_mutex; + + // first try to lock for read-only, if an element for key is found, return it, + // if not, obtain a unique_lock to insert a new element and initialize it. + std::shared_lock read_lock(access_mutex); + auto it = cached_data.find(key); + if (it != cached_data.end()) + return it->second; + else { + read_lock.unlock(); + data_type data = f(key, std::forward(args)...); + std::unique_lock write_lock(access_mutex); + auto new_it = cached_data.emplace(key, std::move(data)); + return new_it.first->second; + } + } + }; + + + template class Policy, class Container> + class ConcurrentCache + : protected Policy + { + using key_type = Key; + using data_type = Data; + + public: + + /// \brief Return the data associated to the `key`. + /** + * Return the data associated to key. If no data is found, create a new entry in the container + * with a value obtained from the functor, by calling `f(key, args...)`. + * + * \param f A functor of signature data_type(key_type, Args...) + * \param args... Arguments passed additionally to the functor f + **/ + template + data_type const& get(key_type const& key, F&& f, Args&&... args) const + { + static_assert(Std::is_callable::value, + "Functor F must have the signature data_type(key_type, Args...)"); + + return ConcurrentCache::get_or_init(key, std::forward(f), std::forward(args)...); + } + }; + +} // end namespace Dune + +#endif // DUNE_COMMON_CONCURRENT_CACHE_HH diff --git a/dune/common/quadmath.hh b/dune/common/quadmath.hh new file mode 100644 index 0000000..ccbe88c --- /dev/null +++ b/dune/common/quadmath.hh @@ -0,0 +1,441 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_QUADMATH_HH +#define DUNE_QUADMATH_HH + +#if HAVE_QUADMATH +#include + +#include +#include +#include +#include // abs +#include +#include +#include +#include + +#include + +namespace Dune +{ + namespace Impl + { + // forward declaration + class Float128; + + } // end namespace Impl + + using Impl::Float128; + + // The purpose of this namespace is to move the `` function overloads + // out of namespace `Dune`, see AlignedNumber in debugalign.hh. + namespace Impl + { + using float128_t = __float128; + + /// Wrapper for quad-precision type __float128 + class Float128 + { + float128_t value_ = 0.0q; + + public: + constexpr Float128() = default; + constexpr Float128(const float128_t& value) noexcept + : value_(value) + {} + + // constructor from any floating-point or integer type + template ::value, int> = 0> + constexpr Float128(const T& value) noexcept + : value_(value) + {} + + // constructor from pointer to null-terminated byte string + Float128(const char* str) noexcept + : value_(strtoflt128(str, NULL)) + {} + + // accessors + constexpr operator float128_t() const noexcept { return value_; } + + constexpr float128_t const& value() const noexcept { return value_; } + constexpr float128_t& value() noexcept { return value_; } + + // I/O + template + friend std::basic_istream& + operator>>(std::basic_istream& in, Float128& x) + { + std::string buf; + buf.reserve(128); + in >> buf; + x.value() = strtoflt128(buf.c_str(), NULL); + return in; + } + + template + friend std::basic_ostream& + operator<<(std::basic_ostream& out, const Float128& x) + { + const std::size_t bufSize = 128; + CharT buf[128]; + + std::string format = "%." + std::to_string(out.precision()) + "Q" + + ((out.flags() | std::ios_base::scientific) ? "e" : "f"); + const int numChars = quadmath_snprintf(buf, bufSize, format.c_str(), x.value()); + if (std::size_t(numChars) >= bufSize) { + DUNE_THROW(Dune::RangeError, "Failed to print Float128 value: buffer overflow"); + } + out << buf; + return out; + } + + // Increment, decrement + constexpr Float128& operator++() noexcept { ++value_; return *this; } + constexpr Float128& operator--() noexcept { --value_; return *this; } + + constexpr Float128 operator++(int) noexcept { Float128 tmp{*this}; ++value_; return tmp; } + constexpr Float128 operator--(int) noexcept { Float128 tmp{*this}; --value_; return tmp; } + + // unary operators + constexpr Float128 operator+() const noexcept { return Float128{+value_}; } + constexpr Float128 operator-() const noexcept { return Float128{-value_}; } + + // assignment operators +#define DUNE_ASSIGN_OP(OP) \ + constexpr Float128& operator OP(const Float128& u) noexcept \ + { \ + value_ OP float128_t(u); \ + return *this; \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_ASSIGN_OP(+=); + DUNE_ASSIGN_OP(-=); + + DUNE_ASSIGN_OP(*=); + DUNE_ASSIGN_OP(/=); + +#undef DUNE_ASSIGN_OP + + }; // end class Float128 + + // binary operators: + // For symmetry provide overloads with arithmetic types + // in the first or second argument. +#define DUNE_BINARY_OP(OP) \ + constexpr Float128 operator OP(const Float128& t, \ + const Float128& u) noexcept \ + { \ + return Float128{float128_t(t) OP float128_t(u)}; \ + } \ + template ::value, int> = 0> \ + constexpr Float128 operator OP(const T& t, \ + const Float128& u) noexcept \ + { \ + return Float128{float128_t(t) OP float128_t(u)}; \ + } \ + template ::value, int> = 0> \ + constexpr Float128 operator OP(const Float128& t, \ + const U& u) noexcept \ + { \ + return Float128{float128_t(t) OP float128_t(u)}; \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_BINARY_OP(+); + DUNE_BINARY_OP(-); + DUNE_BINARY_OP(*); + DUNE_BINARY_OP(/); + +#undef DUNE_BINARY_OP + + // logical operators: + // For symmetry provide overloads with arithmetic types + // in the first or second argument. +#define DUNE_BINARY_BOOL_OP(OP) \ + constexpr bool operator OP(const Float128& t, \ + const Float128& u) noexcept \ + { \ + return float128_t(t) OP float128_t(u); \ + } \ + template ::value, int> = 0> \ + constexpr bool operator OP(const T& t, \ + const Float128& u) noexcept \ + { \ + return float128_t(t) OP float128_t(u); \ + } \ + template ::value, int> = 0> \ + constexpr bool operator OP(const Float128& t, \ + const U& u) noexcept \ + { \ + return float128_t(t) OP float128_t(u); \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_BINARY_BOOL_OP(==); + DUNE_BINARY_BOOL_OP(!=); + DUNE_BINARY_BOOL_OP(<); + DUNE_BINARY_BOOL_OP(>); + DUNE_BINARY_BOOL_OP(<=); + DUNE_BINARY_BOOL_OP(>=); + +#undef DUNE_BINARY_BOOL_OP + + // Overloads for the cmath functions + + // function with name `name` redirects to quadmath function `func` +#define DUNE_UNARY_FUNC(name,func) \ + inline Float128 name(const Float128& u) noexcept \ + { \ + return Float128{func (float128_t(u))}; \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + // like DUNE_UNARY_FUNC but with cutom return type +#define DUNE_CUSTOM_UNARY_FUNC(type,name,func) \ + inline type name(const Float128& u) noexcept \ + { \ + return (type)(func (float128_t(u))); \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + // redirects to quadmath function with two arguments +#define DUNE_BINARY_FUNC(name,func) \ + inline Float128 name(const Float128& t, \ + const Float128& u) noexcept \ + { \ + return Float128{func (float128_t(t), float128_t(u))}; \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_UNARY_FUNC(abs, fabsq); + DUNE_UNARY_FUNC(acos, acosq); + DUNE_UNARY_FUNC(acosh, acoshq); + DUNE_UNARY_FUNC(asin, asinq); + DUNE_UNARY_FUNC(asinh, asinhq); + DUNE_UNARY_FUNC(atan, atanq); + DUNE_UNARY_FUNC(atanh, atanhq); + DUNE_UNARY_FUNC(cbrt, cbrtq); + DUNE_UNARY_FUNC(ceil, ceilq); + DUNE_UNARY_FUNC(cos, cosq); + DUNE_UNARY_FUNC(cosh, coshq); + DUNE_UNARY_FUNC(erf, erfq); + DUNE_UNARY_FUNC(erfc, erfcq); + DUNE_UNARY_FUNC(exp, expq); + DUNE_UNARY_FUNC(expm1, expm1q); + DUNE_UNARY_FUNC(fabs, fabsq); + DUNE_UNARY_FUNC(floor, floorq); + DUNE_CUSTOM_UNARY_FUNC(int, ilogb, ilogbq); + DUNE_UNARY_FUNC(lgamma, lgammaq); + DUNE_CUSTOM_UNARY_FUNC(long long int, llrint, llrintq); + DUNE_CUSTOM_UNARY_FUNC(long long int, llround, llroundq); + DUNE_UNARY_FUNC(log, logq); + DUNE_UNARY_FUNC(log10, log10q); + DUNE_UNARY_FUNC(log1p, log1pq); + DUNE_UNARY_FUNC(log2, log2q); + // DUNE_UNARY_FUNC(logb, logbq); // not available in gcc5 + DUNE_CUSTOM_UNARY_FUNC(long int, lrint, lrintq); + DUNE_CUSTOM_UNARY_FUNC(long int, lround, lroundq); + DUNE_UNARY_FUNC(nearbyint, nearbyintq); + DUNE_BINARY_FUNC(nextafter, nextafterq); + DUNE_BINARY_FUNC(pow, powq); // overload for integer argument see below + DUNE_UNARY_FUNC(rint, rintq); + DUNE_UNARY_FUNC(round, roundq); + DUNE_UNARY_FUNC(sin, sinq); + DUNE_UNARY_FUNC(sinh, sinhq); + DUNE_UNARY_FUNC(sqrt, sqrtq); + DUNE_UNARY_FUNC(tan, tanq); + DUNE_UNARY_FUNC(tanh, tanhq); + DUNE_UNARY_FUNC(tgamma, tgammaq); + DUNE_UNARY_FUNC(trunc, truncq); + + DUNE_CUSTOM_UNARY_FUNC(bool, isfinite, finiteq); + DUNE_CUSTOM_UNARY_FUNC(bool, isinf, isinfq); + DUNE_CUSTOM_UNARY_FUNC(bool, isnan, isnanq); + DUNE_CUSTOM_UNARY_FUNC(bool, signbit, signbitq); + +#undef DUNE_UNARY_FUNC +#undef DUNE_CUSTOM_UNARY_FUNC +#undef DUNE_BINARY_FUNC + + // like DUNE_BINARY_FUNC but provide overloads with arithmetic + // types in the first or second argument. +#define DUNE_BINARY_ARITHMETIC_FUNC(name,func) \ + inline Float128 name(const Float128& t, \ + const Float128& u) noexcept \ + { \ + return Float128{func (float128_t(t), float128_t(u))}; \ + } \ + template ::value, int> = 0> \ + inline Float128 name(const T& t, \ + const Float128& u) noexcept \ + { \ + return Float128{func (float128_t(t), float128_t(u))}; \ + } \ + template ::value, int> = 0> \ + inline Float128 name(const Float128& t, \ + const U& u) noexcept \ + { \ + return Float128{func (float128_t(t), float128_t(u))}; \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_BINARY_ARITHMETIC_FUNC(atan2,atan2q); + DUNE_BINARY_ARITHMETIC_FUNC(copysign,copysignq); + DUNE_BINARY_ARITHMETIC_FUNC(fdim,fdimq); + DUNE_BINARY_ARITHMETIC_FUNC(fmax,fmaxq); + DUNE_BINARY_ARITHMETIC_FUNC(fmin,fminq); + DUNE_BINARY_ARITHMETIC_FUNC(fmod,fmodq); + DUNE_BINARY_ARITHMETIC_FUNC(hypot,hypotq); + DUNE_BINARY_ARITHMETIC_FUNC(remainder,remainderq); + +#undef DUNE_BINARY_ARITHMETIC_FUNC + + // some more cmath functions with special signature + + inline Float128 fma(const Float128& t, const Float128& u, const Float128& v) + { + return Float128{fmaq(float128_t(t),float128_t(u),float128_t(v))}; + } + + inline Float128 frexp(const Float128& u, int* p) + { + return Float128{frexpq(float128_t(u), p)}; + } + + inline Float128 ldexp(const Float128& u, int p) + { + return Float128{ldexpq(float128_t(u), p)}; + } + + inline Float128 remquo(const Float128& t, const Float128& u, int* quo) + { + return Float128{remquoq(float128_t(t), float128_t(u), quo)}; + } + + inline Float128 scalbln(const Float128& u, long int e) + { + return Float128{scalblnq(float128_t(u), e)}; + } + + inline Float128 scalbn(const Float128& u, int e) + { + return Float128{scalbnq(float128_t(u), e)}; + } + + /// \brief Overload of `pow` function for integer exponents. + // NOTE: This is much faster than a pow(x, Float128(p)) call + // NOTE: Derived from the boost::math::cstdfloat::detail::pown implementation + template ::value, int> = 0> + inline Float128 pow(const Float128& x, const Int p) + { + static const Float128 max_value = FLT128_MAX; + static const Float128 min_value = FLT128_MIN; + static const Float128 inf_value = float128_t{1} / float128_t{0}; + + const bool isneg = (x < 0); + const bool isnan = (x != x); + const bool isinf = (isneg ? bool(-x > max_value) : bool(+x > max_value)); + + if (isnan) { return x; } + if (isinf) { return Float128{nanq("")}; } + + const Float128 abs_x = (isneg ? -x : x); + if (p < Int(0)) { + if (abs_x < min_value) + return (isneg ? -inf_value : +inf_value); + else + return Float128(1) / pow(x, Int(-p)); + } + + if (p == Int(0)) { return Float128(1); } + if (p == Int(1)) { return x; } + if (abs_x > max_value) + return (isneg ? -inf_value : +inf_value); + + if (p == Int(2)) { return (x * x); } + if (p == Int(3)) { return ((x * x) * x); } + if (p == Int(4)) { const Float128 x2 = (x * x); return (x2 * x2); } + + Float128 result = ((p % Int(2)) != Int(0)) ? x : Float128(1); + Float128 xn = x; // binary powers of x + + Int p2 = p; + while (Int(p2 /= 2) != Int(0)) { + xn *= xn; // Square xn for each binary power + + const bool has_binary_power = (Int(p2 % Int(2)) != Int(0)); + if (has_binary_power) + result *= xn; + } + + return result; + } + + + } // end namespace Impl + + template <> + struct IsNumber + : public std::true_type {}; + +} // end namespace Dune + +namespace std +{ +#ifndef NO_STD_NUMERIC_LIMITS_SPECIALIZATION + template <> + class numeric_limits + { + using Float128 = Dune::Impl::Float128; + using float128_t = Dune::Impl::float128_t; + + public: + static constexpr bool is_specialized = true; + static constexpr Float128 min() noexcept { return FLT128_MIN; } + static constexpr Float128 max() noexcept { return FLT128_MAX; } + static constexpr Float128 lowest() noexcept { return -FLT128_MAX; } + static constexpr int digits = FLT128_MANT_DIG; + static constexpr int digits10 = 34; + static constexpr int max_digits10 = 36; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr int radix = 2; + static constexpr Float128 epsilon() noexcept { return FLT128_EPSILON; } + static constexpr Float128 round_error() noexcept { return float128_t{0.5}; } + static constexpr int min_exponent = FLT128_MIN_EXP; + static constexpr int min_exponent10 = FLT128_MIN_10_EXP; + static constexpr int max_exponent = FLT128_MAX_EXP; + static constexpr int max_exponent10 = FLT128_MAX_10_EXP; + static constexpr bool has_infinity = true; + static constexpr bool has_quiet_NaN = true; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_present; + static constexpr bool has_denorm_loss = false; + static constexpr Float128 infinity() noexcept { return float128_t{1}/float128_t{0}; } + static Float128 quiet_NaN() noexcept { return nanq(""); } + static constexpr Float128 signaling_NaN() noexcept { return float128_t{}; } + static constexpr Float128 denorm_min() noexcept { return FLT128_DENORM_MIN; } + static constexpr bool is_iec559 = true; + static constexpr bool is_bounded = false; + static constexpr bool is_modulo = false; + static constexpr bool traps = false; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_to_nearest; + }; +#endif +} // end namespace std + +#endif // HAVE_QUADMATH +#endif // DUNE_QUADMATH_HH diff --git a/dune/common/strtonumber.hh b/dune/common/strtonumber.hh new file mode 100644 index 0000000..62bc4ac --- /dev/null +++ b/dune/common/strtonumber.hh @@ -0,0 +1,176 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_COMMON_STRTONUMBER_HH +#define DUNE_COMMON_STRTONUMBER_HH + +#include +#include +#include + +#include +#include +#include + +namespace Dune +{ + /// An exception thrown whenever interpretation of the argument is not possible. + class InvalidArgument : public Exception {}; + + namespace Impl + { + // forward declaration of default parser + template + struct FromCharsParser; + + template + using HasFromChars + = decltype(Std::from_chars(std::declval(), std::declval(), std::declval())); + + + // default behavior: call the string constructor + template + struct StrToNumber + { + static T eval (const char* first, const char* /*last*/) + { + return T(first);; + } + }; + + // if possible, use std::from_chars to convert string to number + template + struct StrToNumber::value>> + { + static T eval (const char* first, const char* last) + { + return FromCharsParser::eval(first, last); + } + }; + + + /// \brief Implementation of a parser using std::from_chars functions + /** + * \tparam T Target numeric type + **/ + template + struct FromCharsParser + { + static T eval (const char* first, const char* last) + { + T value; + auto result = Std::from_chars(first, last, value); + + if (result.ec == std::errc::result_out_of_range) { + DUNE_THROW(RangeError, std::error_condition(result.ec).message()); + } + else if (result.ec == std::errc::invalid_argument) { + DUNE_THROW(InvalidArgument, + "Conversion of '" << first << "' to number failed. Possible reason: invalid string or locale format."); + } + else if (result.ec == std::errc::not_supported) { + DUNE_THROW(NotImplemented, + "Parameter passed to from_chars not supported."); + } + + return value; + } + }; + + /// \brief Implementation of a fallback parser using std::strtoXXX like functions + /** + * Allows leading and trailing whitespace characters. + * NOTE: This parser is not locale independent, but throws an error if whole + * string can not be parsed completely. + * + * \tparam T Target numeric type + **/ + template + struct StrToNumberParser + { + // The parser has the signature `T(const char*, char**)` and may set the errno + // in case of a range error. + template + T eval (const char* first, const char* /*last*/, Parser parser) + { + char* end; + auto old_errno = errno; + errno = 0; + auto x = parser(first, &end); // maybe sets errno + std::swap(errno, old_errno); + + if (old_errno == ERANGE) { + DUNE_THROW(RangeError, std::strerror(old_errno)); + } + + // test whether all non-space characters are consumed during conversion + bool all_consumed = (end != first); + while (all_consumed && (*end != '\0')) + all_consumed = std::isspace(*end++); + + if (!all_consumed) { + DUNE_THROW(InvalidArgument, + "Conversion of '" << first << "' to number failed. Possible reason: invalid string or locale format"); + } + + return convertToRange(x); + } + + private: + // Check whether a numeric conversion is safe + template + static T convertToRange (U const& u) + { + if (sizeof(U) > sizeof(T) && (u > U(std::numeric_limits::max()) || + u < U(std::numeric_limits::min()))) { + DUNE_THROW(RangeError, "Numerical result out of range"); + } + return T(u); + } + }; + + } // end namespace Impl + + + /// \brief Convert a character sequence to a number type `T` + /** + * The cast from character sequence to any numeric type is implemented in terms of the std library function + * `from_chars()` but parametrized with the concrete target type for ease of usage + * in generic code. + * + * Only types that provide a specialization of `Impl::StrToNumber` can call the special functions + * for conversion. All other types by default call the constructor with strings. + * + * The conversion is locale-independent if an overload of from_chars() is found and + * throws in the case of the fallback implementation an \ref InvalidArgument exception if not all + * characters are consumed during conversion, except leading and trailing whitespaces. + * + * In case the represented number is out of range of the number type T, a \ref RangeError exception + * is thrown. + * + * \tparam T The target number type to convert the string to. + * \param str A pointer to the null-terminated byte string to be interpreted. + * + * \throws InvalidArgument + * \throws RangeError + * + * Example of usage: + * \code{.cpp} + * double x = strTo("1.2345"); + * \endcode + **/ + template + T strTo (const char* str) + { + return Impl::StrToNumber::eval(str, str + std::strlen(str)); + } + + /// Overload of \ref strTo for `std::string` arguments. + template + T strTo (const std::string& str) + { + return Impl::StrToNumber::eval(str.c_str(), str.c_str() + str.size()); + } + +} // end namespace Dune + +#endif // DUNE_COMMON_STRTONUMBER_HH diff --git a/dune/common/test/CMakeLists.txt b/dune/common/test/CMakeLists.txt index 1ac638f..5f5e8df 100644 --- a/dune/common/test/CMakeLists.txt +++ b/dune/common/test/CMakeLists.txt @@ -1,3 +1,12 @@ dune_add_test(SOURCES charconvtest.cc + LINK_LIBRARIES dunecommon) + +dune_add_test(SOURCES concurrentcachetest.cc + LINK_LIBRARIES dunecommon) + +dune_add_test(SOURCES quadmathtest.cc LINK_LIBRARIES dunecommon - LABELS quick) + CMAKE_GUARD HAVE_QUADMATH) + +dune_add_test(SOURCES strtonumbertest.cc + LINK_LIBRARIES dunecommon) diff --git a/dune/common/test/charconvtest.cc b/dune/common/test/charconvtest.cc index 1356f56..8616a06 100644 --- a/dune/common/test/charconvtest.cc +++ b/dune/common/test/charconvtest.cc @@ -167,7 +167,7 @@ int main() auto result3 = std::from_chars(bufPtr, result1.ptr, new_value2, base); test.check(bool(new_value == new_value2), "comparison with std::from_chars"); #else - std::setlocale(LC_NUMERIC, "C"); + setlocale(LC_NUMERIC, "C"); T new_value2; if (std::is_unsigned::value) new_value2 = std::strtoull(bufPtr, nullptr, base); diff --git a/dune/common/test/concurrentcachetest.cc b/dune/common/test/concurrentcachetest.cc new file mode 100644 index 0000000..786bca7 --- /dev/null +++ b/dune/common/test/concurrentcachetest.cc @@ -0,0 +1,98 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace Dune +{ + struct quadrature_key + { + int id; // topologyId + int p; // order + int qt; // quadrature type + + struct hasher + { + std::size_t operator()(quadrature_key const& t) const + { + std::size_t seed = 0; + hash_combine(seed, t.id); + hash_combine(seed, t.p); + hash_combine(seed, t.qt); + return seed; + } + }; + + friend bool operator==(quadrature_key const& lhs, quadrature_key const& rhs) + { + return std::tie(lhs.id,lhs.p,lhs.qt) == std::tie(rhs.id,rhs.p,rhs.qt); + } + }; + +} // end namespace Dune + + +using quadrature_key = Dune::quadrature_key; +using quadrature_data = std::vector; + +quadrature_data init_data(quadrature_key const& key) +{ + quadrature_data data(100); + std::generate(data.begin(), data.end(), []{ return std::fmod(double(std::rand()), 10.0); }); + std::stringstream ss; + ss << "init [" << key.id << "," << key.p << "," << key.qt << "]\n"; + std::cout << ss.str(); + return data; +} + +template