Commit 45dfcf78 authored by Praetorius, Simon's avatar Praetorius, Simon

added implementation for quadmath, concurrentcache and strtonumber

parent 8b4077cc
Pipeline #1602 failed with stage
in 4 minutes and 24 seconds
# 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)
set(modules "DuneCommonExtensionsMacros.cmake")
find_package(QuadMath)
include(AddQuadMathFlags)
install(FILES ${modules} DESTINATION ${DUNE_INSTALL_MODULEDIR})
# .. 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 <quadmath.h>
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()
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
// -*- 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 <mutex>
#include <shared_mutex>
#include <thread>
#include <tuple>
#include <unordered_map>
#include <dune/common/hash.hh>
#include <dune/common/std/type_traits.hh>
namespace Dune
{
/// Store cache in instance.
template <class Container>
struct ConsecutivePolicy;
/// Store cache thread local, requires no locking.
template <class Container>
struct ThreadLocalPolicy;
/// Stores cache global static, requires locking on write access.
template <class Container>
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<Key,Data>`
* 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 Key,
class Data,
template <class> class Policy = ThreadLocalPolicy,
class Container = std::unordered_map<Key, Data>>
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 Container>
class ConcurrentCachePolicy;
#endif
// implementation of the consecutive policy. Data is stored in instance variable.
template <class Container>
struct ConsecutivePolicy
{
using key_type = typename Container::key_type;
using data_type = typename Container::mapped_type;
using container_type = Container;
template <class F, class... Args>
data_type const& get_or_init(key_type const& key, F&& f, Args&&... args) const
{
return impl(std::is_default_constructible<data_type>{},
key, std::forward<F>(f), std::forward<Args>(args)...);
}
private:
// data_type is default_constructible
template <class F, class... Args>
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>(args)...);
it.first->second = std::move(data);
}
return it.first->second;
}
// data_type is not default_constructible
template <class F, class... Args>
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>(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 <class Container>
struct ThreadLocalPolicy
{
using key_type = typename Container::key_type;
using data_type = typename Container::mapped_type;
using container_type = Container;
template <class F, class... Args>
static data_type const& get_or_init(key_type const& key, F&& f, Args&&... args)
{
return impl(std::is_default_constructible<data_type>{},
key, std::forward<F>(f), std::forward<Args>(args)...);
}
private:
// data_type is default_constructible
template <class F, class... Args>
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>(args)...);
it.first->second = std::move(data);
}
return it.first->second;
}
// data_type is not default_constructible
template <class F, class... Args>
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>(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 <class Container>
struct StaticLockedPolicy
{
using key_type = typename Container::key_type;
using data_type = typename Container::mapped_type;
using container_type = Container;
template <class F, class... Args>
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<mutex_type> 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>(args)...);
std::unique_lock<mutex_type> write_lock(access_mutex);
auto new_it = cached_data.emplace(key, std::move(data));
return new_it.first->second;
}
}
};
template <class Key, class Data, template <class> class Policy, class Container>
class ConcurrentCache
: protected Policy<Container>
{
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 <class F, class... Args>
data_type const& get(key_type const& key, F&& f, Args&&... args) const
{
static_assert(Std::is_callable<F(key_type, Args...), data_type>::value,
"Functor F must have the signature data_type(key_type, Args...)");
return ConcurrentCache::get_or_init(key, std::forward<F>(f), std::forward<Args>(args)...);
}
};
} // end namespace Dune
#endif // DUNE_COMMON_CONCURRENT_CACHE_HH
// -*- 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 <quadmath.h>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstdlib> // abs
#include <istream>
#include <ostream>
#include <type_traits>
#include <utility>
#include <dune/common/typetraits.hh>
namespace Dune
{
namespace Impl
{
// forward declaration
class Float128;
} // end namespace Impl
using Impl::Float128;
// The purpose of this namespace is to move the `<cmath>` 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 <class T,
std::enable_if_t<std::is_arithmetic<T>::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<class CharT, class Traits>
friend std::basic_istream<CharT, Traits>&
operator>>(std::basic_istream<CharT, Traits>& in, Float128& x)
{
std::string buf;
buf.reserve(128);
in >> buf;
x.value() = strtoflt128(buf.c_str(), NULL);
return in;
}
template<class CharT, class Traits>
friend std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& 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 <class T, \
std::enable_if_t<std::is_arithmetic<T>::value, int> = 0> \
constexpr Float128 operator OP(const T& t, \
const Float128& u) noexcept \
{ \
return Float128{float128_t(t) OP float128_t(u)}; \
} \
template <class U, \
std::enable_if_t<std::is_arithmetic<U>::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 <class T, \
std::enable_if_t<std::is_arithmetic<T>::value, int> = 0> \
constexpr bool operator OP(const T& t, \
const Float128& u) noexcept \
{ \
return float128_t(t) OP float128_t(u); \
} \
template <class U, \
std::enable_if_t<std::is_arithmetic<U>::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 <class T, \
std::enable_if_t<std::is_arithmetic<T>::value, int> = 0> \
inline Float128 name(const T& t, \
const Float128& u) noexcept \
{ \
return Float128{func (float128_t(t), float128_t(u))}; \
} \
template <class U, \
std::enable_if_t<std::is_arithmetic<U>::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 <class Int,
std::enable_if_t<std::is_integral<Int>::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;
}