aboutsummaryrefslogtreecommitdiff
path: root/third-party/boost-math/include/boost/math/tools/test_value.hpp
blob: c44fe052894fe6102b5ee89b6b2b65dbd58ad280 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Copyright Paul A. Bristow 2017.
// Copyright John Maddock 2017.

// Use, modification and distribution are subject to the
// Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)

// test_value.hpp

#ifndef TEST_VALUE_HPP
#define TEST_VALUE_HPP

// BOOST_MATH_TEST_VALUE is used to create a test value of suitable type from a decimal digit string.
// Two parameters, both a floating-point literal double like 1.23 (not long double so no suffix L)
// and a decimal digit string const char* like "1.23" must be provided.
// The decimal value represented must be the same of course, with at least enough precision for long double.
//   Note there are two gotchas to this approach:
// * You need all values to be real floating-point values
// * and *MUST* include a decimal point (to avoid confusion with an integer literal).
// * It's slow to compile compared to a simple literal.

// Speed is not an issue for a few test values,
// but it's not generally usable in large tables
// where you really need everything to be statically initialized.

// Macro BOOST_MATH_INSTRUMENT_CREATE_TEST_VALUE provides a global diagnostic value for create_type.

#include <boost/cstdfloat.hpp> // For float_64_t, float128_t. Must be first include!
#ifndef BOOST_MATH_STANDALONE
#include <boost/lexical_cast.hpp>
#endif
#include <limits>
#include <type_traits>

#ifdef BOOST_MATH_INSTRUMENT_CREATE_TEST_VALUE
// global int create_type(0); must be defined before including this file.
#endif

#ifdef BOOST_HAS_FLOAT128
typedef __float128 largest_float;
#define BOOST_MATH_TEST_LARGEST_FLOAT_SUFFIX(x) x##Q
#define BOOST_MATH_TEST_LARGEST_FLOAT_DIGITS 113
#else
typedef long double largest_float;
#define BOOST_MATH_TEST_LARGEST_FLOAT_SUFFIX(x) x##L
#define BOOST_MATH_TEST_LARGEST_FLOAT_DIGITS std::numeric_limits<long double>::digits
#endif

template <class T, class T2>
inline T create_test_value(largest_float val, const char*, const std::true_type&, const T2&)
{ // Construct from long double or quad parameter val (ignoring string/const char* str).
  // (This is case for MPL parameters = true_ and T2 == false_,
  // and  MPL parameters = true_ and T2 == true_  cpp_bin_float)
  // All built-in/fundamental floating-point types,
  // and other User-Defined Types that can be constructed without loss of precision
  // from long double suffix L (or quad suffix Q),
  //
  // Choose this method, even if can be constructed from a string,
  // because it will be faster, and more likely to be the closest representation.
  // (This is case for MPL parameters = true_type and T2 == true_type).
  #ifdef BOOST_MATH_INSTRUMENT_CREATE_TEST_VALUE
  create_type = 1;
  #endif
  return static_cast<T>(val);
}

template <class T>
inline T create_test_value(largest_float, const char* str, const std::false_type&, const std::true_type&)
{ // Construct from decimal digit string const char* @c str (ignoring long double parameter).
  // For example, extended precision or other User-Defined types which ARE constructible from a string
  // (but not from double, or long double without loss of precision).
  // (This is case for MPL parameters = false_type and T2 == true_type).
  #ifdef BOOST_MATH_INSTRUMENT_CREATE_TEST_VALUE
  create_type = 2;
  #endif
  return T(str);
}

template <class T>
inline T create_test_value(largest_float, const char* str, const std::false_type&, const std::false_type&)
{ // Create test value using from lexical cast of decimal digit string const char* str.
  // For example, extended precision or other User-Defined types which are NOT constructible from a string
  // (NOR constructible from a long double).
  // (This is case T1 = false_type and T2 == false_type).
#ifdef BOOST_MATH_INSTRUMENT_CREATE_TEST_VALUE
  create_type = 3;
#endif
#if defined(BOOST_MATH_STANDALONE)
  static_assert(sizeof(T) == 0, "Can not create a test value using lexical cast of string in standalone mode");
  return T();
#else
  return boost::lexical_cast<T>(str);
#endif
}

// T real type, x a decimal digits representation of a floating-point, for example: 12.34.
// It must include a decimal point (or it would be interpreted as an integer).

//  x is converted to a long double by appending the letter L (to suit long double fundamental type), 12.34L.
//  x is also passed as a const char* or string representation "12.34"
//  (to suit most other types that cannot be constructed from long double without possible loss).

// BOOST_MATH_TEST_LARGEST_FLOAT_SUFFIX(x) makes a long double or quad version, with
// suffix a letter L (or Q) to suit long double (or quad) fundamental type, 12.34L or 12.34Q.
// #x makes a decimal digit string version to suit multiprecision and fixed_point constructors, "12.34".
// (Constructing from double or long double (or quad) could lose precision for multiprecision or fixed-point).

// The matching create_test_value function above is chosen depending on the T1 and T2 mpl bool truths.
// The string version from #x is used if the precision of T is greater than long double.

// Example: long double test_value = BOOST_MATH_TEST_VALUE(double, 1.23456789);

#define BOOST_MATH_TEST_VALUE(T, x) create_test_value<T>(\
  BOOST_MATH_TEST_LARGEST_FLOAT_SUFFIX(x),\
  #x,\
  std::integral_constant<bool, \
    std::numeric_limits<T>::is_specialized &&\
      (std::numeric_limits<T>::radix == 2)\
        && (std::numeric_limits<T>::digits <= BOOST_MATH_TEST_LARGEST_FLOAT_DIGITS)\
        && (std::is_convertible<largest_float, T>::value || std::is_floating_point<T>::value)>(),\
  std::integral_constant<bool, \
    std::is_constructible<T, const char*>::value>()\
)

#if LDBL_MAX_10_EXP > DBL_MAX_10_EXP
#define BOOST_MATH_TEST_HUGE_FLOAT_SUFFIX(x) BOOST_MATH_TEST_LARGEST_FLOAT_SUFFIX(x)
#else
#define BOOST_MATH_TEST_HUGE_FLOAT_SUFFIX(x) 0.0
#endif

#define BOOST_MATH_HUGE_TEST_VALUE(T, x) create_test_value<T>(\
  BOOST_MATH_TEST_HUGE_FLOAT_SUFFIX(x),\
  #x,\
  std::integral_constant<bool, \
    std::numeric_limits<T>::is_specialized &&\
      (std::numeric_limits<T>::radix == 2)\
        && (std::numeric_limits<T>::digits <= BOOST_MATH_TEST_LARGEST_FLOAT_DIGITS)\
        && std::is_convertible<largest_float, T>::value>(),\
  std::integral_constant<bool, \
    std::is_constructible<T, const char*>::value>()\
)
#endif // TEST_VALUE_HPP