Merge branch 'master' into ci
This commit is contained in:
commit
739af475b3
@ -56,7 +56,7 @@ def create_build_env(dirname='virtualenv'):
|
|||||||
pip_install('sphinx-doc/sphinx', '12b83372ac9316e8cbe86e7fed889296a4cc29ee',
|
pip_install('sphinx-doc/sphinx', '12b83372ac9316e8cbe86e7fed889296a4cc29ee',
|
||||||
min_version='1.4.1.dev20160531')
|
min_version='1.4.1.dev20160531')
|
||||||
pip_install('michaeljones/breathe',
|
pip_install('michaeljones/breathe',
|
||||||
'6b1c5bb7a1866f15fc328b8716258354b10c1daa',
|
'129222318f7c8f865d2631e7da7b033567e7f56a',
|
||||||
min_version='4.2.0')
|
min_version='4.2.0')
|
||||||
|
|
||||||
def build_docs(version='dev', **kwargs):
|
def build_docs(version='dev', **kwargs):
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
||||||
#define FMT_VERSION 50000
|
#define FMT_VERSION 50100
|
||||||
|
|
||||||
#ifdef __has_feature
|
#ifdef __has_feature
|
||||||
# define FMT_HAS_FEATURE(x) __has_feature(x)
|
# define FMT_HAS_FEATURE(x) __has_feature(x)
|
||||||
@ -804,8 +804,9 @@ class arg_map {
|
|||||||
~arg_map() { delete [] map_; }
|
~arg_map() { delete [] map_; }
|
||||||
|
|
||||||
basic_format_arg<Context> find(basic_string_view<char_type> name) const {
|
basic_format_arg<Context> find(basic_string_view<char_type> name) const {
|
||||||
|
auto end = map_ + size_; // Define end outside of loop to make nvcc happy.
|
||||||
// The list is unsorted, so just return the first matching name.
|
// The list is unsorted, so just return the first matching name.
|
||||||
for (auto it = map_, end = map_ + size_; it != end; ++it) {
|
for (auto it = map_; it != end; ++it) {
|
||||||
if (it->name == name)
|
if (it->name == name)
|
||||||
return it->arg;
|
return it->arg;
|
||||||
}
|
}
|
||||||
@ -990,16 +991,17 @@ class format_arg_store {
|
|||||||
|
|
||||||
friend class basic_format_args<Context>;
|
friend class basic_format_args<Context>;
|
||||||
|
|
||||||
static FMT_CONSTEXPR uint64_t get_types() {
|
static FMT_CONSTEXPR int64_t get_types() {
|
||||||
return IS_PACKED ? internal::get_types<Context, Args...>()
|
return IS_PACKED ?
|
||||||
: -static_cast<int64_t>(NUM_ARGS);
|
static_cast<int64_t>(internal::get_types<Context, Args...>()) :
|
||||||
|
-static_cast<int64_t>(NUM_ARGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
#if FMT_USE_CONSTEXPR
|
#if FMT_USE_CONSTEXPR
|
||||||
static constexpr uint64_t TYPES = get_types();
|
static constexpr int64_t TYPES = get_types();
|
||||||
#else
|
#else
|
||||||
static const uint64_t TYPES;
|
static const int64_t TYPES;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 405
|
#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 405
|
||||||
@ -1015,7 +1017,7 @@ class format_arg_store {
|
|||||||
|
|
||||||
#if !FMT_USE_CONSTEXPR
|
#if !FMT_USE_CONSTEXPR
|
||||||
template <typename Context, typename ...Args>
|
template <typename Context, typename ...Args>
|
||||||
const uint64_t format_arg_store<Context, Args...>::TYPES = get_types();
|
const int64_t format_arg_store<Context, Args...>::TYPES = get_types();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -223,30 +223,19 @@ FMT_FUNC void system_error::init(
|
|||||||
namespace internal {
|
namespace internal {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
int char_traits<char>::format_float(
|
int char_traits<char>::format_float(
|
||||||
char *buffer, std::size_t size, const char *format,
|
char *buffer, std::size_t size, const char *format, int precision, T value) {
|
||||||
unsigned width, int precision, T value) {
|
|
||||||
if (width == 0) {
|
|
||||||
return precision < 0 ?
|
|
||||||
FMT_SNPRINTF(buffer, size, format, value) :
|
|
||||||
FMT_SNPRINTF(buffer, size, format, precision, value);
|
|
||||||
}
|
|
||||||
return precision < 0 ?
|
return precision < 0 ?
|
||||||
FMT_SNPRINTF(buffer, size, format, width, value) :
|
FMT_SNPRINTF(buffer, size, format, value) :
|
||||||
FMT_SNPRINTF(buffer, size, format, width, precision, value);
|
FMT_SNPRINTF(buffer, size, format, precision, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
int char_traits<wchar_t>::format_float(
|
int char_traits<wchar_t>::format_float(
|
||||||
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
wchar_t *buffer, std::size_t size, const wchar_t *format, int precision,
|
||||||
unsigned width, int precision, T value) {
|
T value) {
|
||||||
if (width == 0) {
|
|
||||||
return precision < 0 ?
|
|
||||||
FMT_SWPRINTF(buffer, size, format, value) :
|
|
||||||
FMT_SWPRINTF(buffer, size, format, precision, value);
|
|
||||||
}
|
|
||||||
return precision < 0 ?
|
return precision < 0 ?
|
||||||
FMT_SWPRINTF(buffer, size, format, width, value) :
|
FMT_SWPRINTF(buffer, size, format, value) :
|
||||||
FMT_SWPRINTF(buffer, size, format, width, precision, value);
|
FMT_SWPRINTF(buffer, size, format, precision, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -285,35 +274,28 @@ const uint64_t basic_data<T>::POWERS_OF_10_64[] = {
|
|||||||
// These are generated by support/compute-powers.py.
|
// These are generated by support/compute-powers.py.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const uint64_t basic_data<T>::POW10_SIGNIFICANDS[] = {
|
const uint64_t basic_data<T>::POW10_SIGNIFICANDS[] = {
|
||||||
0xfa8fd5a0081c0288ull, 0xbaaee17fa23ebf76ull, 0x8b16fb203055ac76ull,
|
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, 0xcf42894a5dce35ea,
|
||||||
0xcf42894a5dce35eaull, 0x9a6bb0aa55653b2dull, 0xe61acf033d1a45dfull,
|
0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f,
|
||||||
0xab70fe17c79ac6caull, 0xff77b1fcbebcdc4full, 0xbe5691ef416bd60cull,
|
0xbe5691ef416bd60c, 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
|
||||||
0x8dd01fad907ffc3cull, 0xd3515c2831559a83ull, 0x9d71ac8fada6c9b5ull,
|
0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, 0xc21094364dfb5637,
|
||||||
0xea9c227723ee8bcbull, 0xaecc49914078536dull, 0x823c12795db6ce57ull,
|
0x9096ea6f3848984f, 0xd77485cb25823ac7, 0xa086cfcd97bf97f4, 0xef340a98172aace5,
|
||||||
0xc21094364dfb5637ull, 0x9096ea6f3848984full, 0xd77485cb25823ac7ull,
|
0xb23867fb2a35b28e, 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
|
||||||
0xa086cfcd97bf97f4ull, 0xef340a98172aace5ull, 0xb23867fb2a35b28eull,
|
0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, 0xb5b5ada8aaff80b8,
|
||||||
0x84c8d4dfd2c63f3bull, 0xc5dd44271ad3cdbaull, 0x936b9fcebb25c996ull,
|
0x87625f056c7c4a8b, 0xc9bcff6034c13053, 0x964e858c91ba2655, 0xdff9772470297ebd,
|
||||||
0xdbac6c247d62a584ull, 0xa3ab66580d5fdaf6ull, 0xf3e2f893dec3f126ull,
|
0xa6dfbd9fb8e5b88f, 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
|
||||||
0xb5b5ada8aaff80b8ull, 0x87625f056c7c4a8bull, 0xc9bcff6034c13053ull,
|
0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, 0xaa242499697392d3,
|
||||||
0x964e858c91ba2655ull, 0xdff9772470297ebdull, 0xa6dfbd9fb8e5b88full,
|
0xfd87b5f28300ca0e, 0xbce5086492111aeb, 0x8cbccc096f5088cc, 0xd1b71758e219652c,
|
||||||
0xf8a95fcf88747d94ull, 0xb94470938fa89bcfull, 0x8a08f0f8bf0f156bull,
|
0x9c40000000000000, 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
|
||||||
0xcdb02555653131b6ull, 0x993fe2c6d07b7facull, 0xe45c10c42a2b3b06ull,
|
0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, 0x9f4f2726179a2245,
|
||||||
0xaa242499697392d3ull, 0xfd87b5f28300ca0eull, 0xbce5086492111aebull,
|
0xed63a231d4c4fb27, 0xb0de65388cc8ada8, 0x83c7088e1aab65db, 0xc45d1df942711d9a,
|
||||||
0x8cbccc096f5088ccull, 0xd1b71758e219652cull, 0x9c40000000000000ull,
|
0x924d692ca61be758, 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
|
||||||
0xe8d4a51000000000ull, 0xad78ebc5ac620000ull, 0x813f3978f8940984ull,
|
0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, 0x952ab45cfa97a0b3,
|
||||||
0xc097ce7bc90715b3ull, 0x8f7e32ce7bea5c70ull, 0xd5d238a4abe98068ull,
|
0xde469fbd99a05fe3, 0xa59bc234db398c25, 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece,
|
||||||
0x9f4f2726179a2245ull, 0xed63a231d4c4fb27ull, 0xb0de65388cc8ada8ull,
|
0x88fcf317f22241e2, 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
|
||||||
0x83c7088e1aab65dbull, 0xc45d1df942711d9aull, 0x924d692ca61be758ull,
|
0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, 0x8bab8eefb6409c1a,
|
||||||
0xda01ee641a708deaull, 0xa26da3999aef774aull, 0xf209787bb47d6b85ull,
|
0xd01fef10a657842c, 0x9b10a4e5e9913129, 0xe7109bfba19c0c9d, 0xac2820d9623bf429,
|
||||||
0xb454e4a179dd1877ull, 0x865b86925b9bc5c2ull, 0xc83553c5c8965d3dull,
|
0x80444b5e7aa7cf85, 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
|
||||||
0x952ab45cfa97a0b3ull, 0xde469fbd99a05fe3ull, 0xa59bc234db398c25ull,
|
0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b
|
||||||
0xf6c69a72a3989f5cull, 0xb7dcbf5354e9beceull, 0x88fcf317f22241e2ull,
|
|
||||||
0xcc20ce9bd35c78a5ull, 0x98165af37b2153dfull, 0xe2a0b5dc971f303aull,
|
|
||||||
0xa8d9d1535ce3b396ull, 0xfb9b7cd9a4a7443cull, 0xbb764c4ca7a44410ull,
|
|
||||||
0x8bab8eefb6409c1aull, 0xd01fef10a657842cull, 0x9b10a4e5e9913129ull,
|
|
||||||
0xe7109bfba19c0c9dull, 0xac2820d9623bf429ull, 0x80444b5e7aa7cf85ull,
|
|
||||||
0xbf21e44003acdd2dull, 0x8e679c2f5e44ff8full, 0xd433179d9c8cb841ull,
|
|
||||||
0x9e19db92b4e31ba9ull, 0xeb96bf6ebadf77d9ull, 0xaf87023b9bf0ee6bull
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
|
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
|
||||||
@ -340,6 +322,19 @@ FMT_FUNC fp operator*(fp x, fp y) {
|
|||||||
uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
|
uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
|
||||||
return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), x.e + y.e + 64);
|
return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), x.e + y.e + 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) {
|
||||||
|
const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10)
|
||||||
|
int index = static_cast<int>(std::ceil(
|
||||||
|
(min_exponent + fp::significand_size - 1) * one_over_log2_10));
|
||||||
|
// Decimal exponent of the first (smallest) cached power of 10.
|
||||||
|
const int first_dec_exp = -348;
|
||||||
|
// Difference between two consecutive decimal exponents in cached powers of 10.
|
||||||
|
const int dec_exp_step = 8;
|
||||||
|
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
|
||||||
|
pow10_exponent = first_dec_exp + index * dec_exp_step;
|
||||||
|
return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]);
|
||||||
|
}
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
#if FMT_USE_WINDOWS_H
|
#if FMT_USE_WINDOWS_H
|
||||||
|
|||||||
@ -88,7 +88,23 @@
|
|||||||
|
|
||||||
#ifndef FMT_THROW
|
#ifndef FMT_THROW
|
||||||
# if FMT_EXCEPTIONS
|
# if FMT_EXCEPTIONS
|
||||||
# define FMT_THROW(x) throw x
|
# if FMT_MSC_VER
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
namespace internal {
|
||||||
|
template <typename Exception>
|
||||||
|
inline void do_throw(const Exception &x) {
|
||||||
|
// Silence unreachable code warnings in MSVC because these are nearly
|
||||||
|
// impossible to fix in a generic code.
|
||||||
|
volatile bool b = true;
|
||||||
|
if (b)
|
||||||
|
throw x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
# define FMT_THROW(x) fmt::internal::do_throw(x)
|
||||||
|
# else
|
||||||
|
# define FMT_THROW(x) throw x
|
||||||
|
# endif
|
||||||
# else
|
# else
|
||||||
# define FMT_THROW(x) do { static_cast<void>(sizeof(x)); assert(false); } while(false);
|
# define FMT_THROW(x) do { static_cast<void>(sizeof(x)); assert(false); } while(false);
|
||||||
# endif
|
# endif
|
||||||
@ -105,8 +121,9 @@
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if FMT_USE_USER_DEFINED_LITERALS && __cplusplus >= 201402L && \
|
#if FMT_USE_USER_DEFINED_LITERALS && !defined(FMT_ICC_VERSION) && \
|
||||||
(FMT_GCC_VERSION >= 600 || FMT_CLANG_VERSION >= 304)
|
((FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L) || \
|
||||||
|
(defined(FMT_CLANG_VERSION) && FMT_CLANG_VERSION >= 304))
|
||||||
# define FMT_UDL_TEMPLATE 1
|
# define FMT_UDL_TEMPLATE 1
|
||||||
#else
|
#else
|
||||||
# define FMT_UDL_TEMPLATE 0
|
# define FMT_UDL_TEMPLATE 0
|
||||||
@ -122,7 +139,8 @@
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if FMT_HAS_GXX_CXX11 || FMT_HAS_FEATURE(cxx_trailing_return) || FMT_MSC_VER >= 1600
|
#if FMT_HAS_GXX_CXX11 || FMT_HAS_FEATURE(cxx_trailing_return) || \
|
||||||
|
FMT_MSC_VER >= 1600
|
||||||
# define FMT_USE_TRAILING_RETURN 1
|
# define FMT_USE_TRAILING_RETURN 1
|
||||||
#else
|
#else
|
||||||
# define FMT_USE_TRAILING_RETURN 0
|
# define FMT_USE_TRAILING_RETURN 0
|
||||||
@ -134,6 +152,10 @@
|
|||||||
# define FMT_USE_RVALUE_REFERENCES 0
|
# define FMT_USE_RVALUE_REFERENCES 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef FMT_USE_GRISU
|
||||||
|
# define FMT_USE_GRISU 0
|
||||||
|
#endif
|
||||||
|
|
||||||
// __builtin_clz is broken in clang with Microsoft CodeGen:
|
// __builtin_clz is broken in clang with Microsoft CodeGen:
|
||||||
// https://github.com/fmtlib/fmt/issues/519
|
// https://github.com/fmtlib/fmt/issues/519
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
@ -256,10 +278,24 @@ inline dummy_int isnan(...) { return dummy_int(); }
|
|||||||
inline dummy_int _isnan(...) { return dummy_int(); }
|
inline dummy_int _isnan(...) { return dummy_int(); }
|
||||||
|
|
||||||
// A handmade floating-point number f * pow(2, e).
|
// A handmade floating-point number f * pow(2, e).
|
||||||
struct fp {
|
class fp {
|
||||||
uint64_t f;
|
private:
|
||||||
|
typedef uint64_t significand_type;
|
||||||
|
|
||||||
|
// All sizes are in bits.
|
||||||
|
static constexpr int char_size = std::numeric_limits<unsigned char>::digits;
|
||||||
|
// Subtract 1 to account for an implicit most significant bit in the
|
||||||
|
// normalized form.
|
||||||
|
static constexpr int double_significand_size =
|
||||||
|
std::numeric_limits<double>::digits - 1;
|
||||||
|
static constexpr uint64_t implicit_bit = 1ull << double_significand_size;
|
||||||
|
|
||||||
|
public:
|
||||||
|
significand_type f;
|
||||||
int e;
|
int e;
|
||||||
|
|
||||||
|
static constexpr int significand_size = sizeof(significand_type) * char_size;
|
||||||
|
|
||||||
fp(uint64_t f, int e): f(f), e(e) {}
|
fp(uint64_t f, int e): f(f), e(e) {}
|
||||||
|
|
||||||
// Constructs fp from an IEEE754 double. It is a template to prevent compile
|
// Constructs fp from an IEEE754 double. It is a template to prevent compile
|
||||||
@ -268,24 +304,35 @@ struct fp {
|
|||||||
explicit fp(Double d) {
|
explicit fp(Double d) {
|
||||||
// Assume double is in the format [sign][exponent][significand].
|
// Assume double is in the format [sign][exponent][significand].
|
||||||
typedef std::numeric_limits<Double> limits;
|
typedef std::numeric_limits<Double> limits;
|
||||||
const int double_size =
|
const int double_size = sizeof(Double) * char_size;
|
||||||
sizeof(Double) * std::numeric_limits<unsigned char>::digits;
|
const int exponent_size =
|
||||||
// Subtract 1 to account for an implicit most significant bit in the
|
double_size - double_significand_size - 1; // -1 for sign
|
||||||
// normalized form.
|
|
||||||
const int significand_size = limits::digits - 1;
|
|
||||||
const int exponent_size = double_size - significand_size - 1; // -1 for sign
|
|
||||||
const uint64_t implicit_bit = 1ull << significand_size;
|
|
||||||
const uint64_t significand_mask = implicit_bit - 1;
|
const uint64_t significand_mask = implicit_bit - 1;
|
||||||
const uint64_t exponent_mask = (~0ull >> 1) & ~significand_mask;
|
const uint64_t exponent_mask = (~0ull >> 1) & ~significand_mask;
|
||||||
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
|
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
|
||||||
auto u = bit_cast<uint64_t>(d);
|
auto u = bit_cast<uint64_t>(d);
|
||||||
auto biased_e = (u & exponent_mask) >> significand_size;
|
auto biased_e = (u & exponent_mask) >> double_significand_size;
|
||||||
f = u & significand_mask;
|
f = u & significand_mask;
|
||||||
if (biased_e != 0)
|
if (biased_e != 0)
|
||||||
f += implicit_bit;
|
f += implicit_bit;
|
||||||
else
|
else
|
||||||
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
|
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
|
||||||
e = biased_e - exponent_bias - significand_size;
|
e = biased_e - exponent_bias - double_significand_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
|
||||||
|
template <int SHIFT = 0>
|
||||||
|
void normalize() {
|
||||||
|
// Handle subnormals.
|
||||||
|
auto shifted_implicit_bit = implicit_bit << SHIFT;
|
||||||
|
while ((f & shifted_implicit_bit) == 0) {
|
||||||
|
f <<= 1;
|
||||||
|
--e;
|
||||||
|
}
|
||||||
|
// Subtract 1 to account for hidden bit.
|
||||||
|
auto offset = significand_size - double_significand_size - SHIFT - 1;
|
||||||
|
f <<= offset;
|
||||||
|
e -= offset;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -295,16 +342,13 @@ inline fp operator-(fp x, fp y) {
|
|||||||
return fp(x.f - y.f, x.e);
|
return fp(x.f - y.f, x.e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computes an fp number r with r.f = x.f * y.f / pow(2, 32) rounded to nearest
|
// Computes an fp number r with r.f = x.f * y.f / pow(2, 64) rounded to nearest
|
||||||
// with half-up tie breaking, r.e = x.e + y.e + 32. Result may not be normalized.
|
// with half-up tie breaking, r.e = x.e + y.e + 64. Result may not be normalized.
|
||||||
fp operator*(fp x, fp y);
|
fp operator*(fp x, fp y);
|
||||||
|
|
||||||
// Compute k such that its cached power c_k = c_k.f * pow(2, c_k.e) satisfies
|
// Returns cached power (of 10) c_k = c_k.f * pow(2, c_k.e) such that its
|
||||||
// min_exponent <= c_k.e + e <= min_exponent + 3.
|
// (binary) exponent satisfies min_exponent <= c_k.e <= min_exponent + 3.
|
||||||
inline int compute_cached_power_index(int e, int min_exponent) {
|
fp get_cached_power(int min_exponent, int &pow10_exponent);
|
||||||
FMT_CONSTEXPR_DECL double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10)
|
|
||||||
return static_cast<int>(std::ceil((min_exponent - e + 63) * one_over_log2_10));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Allocator>
|
template <typename Allocator>
|
||||||
typename Allocator::value_type *allocate(Allocator& alloc, std::size_t n) {
|
typename Allocator::value_type *allocate(Allocator& alloc, std::size_t n) {
|
||||||
@ -370,6 +414,38 @@ FMT_BEGIN_NAMESPACE
|
|||||||
template <typename Range>
|
template <typename Range>
|
||||||
class basic_writer;
|
class basic_writer;
|
||||||
|
|
||||||
|
template <typename OutputIt, typename T = typename OutputIt::value_type>
|
||||||
|
class output_range {
|
||||||
|
private:
|
||||||
|
OutputIt it_;
|
||||||
|
|
||||||
|
// Unused yet.
|
||||||
|
typedef void sentinel;
|
||||||
|
sentinel end() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef OutputIt iterator;
|
||||||
|
typedef T value_type;
|
||||||
|
|
||||||
|
explicit output_range(OutputIt it): it_(it) {}
|
||||||
|
OutputIt begin() const { return it_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// A range where begin() returns back_insert_iterator.
|
||||||
|
template <typename Container>
|
||||||
|
class back_insert_range:
|
||||||
|
public output_range<std::back_insert_iterator<Container>> {
|
||||||
|
typedef output_range<std::back_insert_iterator<Container>> base;
|
||||||
|
public:
|
||||||
|
typedef typename Container::value_type value_type;
|
||||||
|
|
||||||
|
back_insert_range(Container &c): base(std::back_inserter(c)) {}
|
||||||
|
back_insert_range(typename base::iterator it): base(it) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef basic_writer<back_insert_range<internal::buffer>> writer;
|
||||||
|
typedef basic_writer<back_insert_range<internal::wbuffer>> wwriter;
|
||||||
|
|
||||||
/** A formatting error such as invalid format string. */
|
/** A formatting error such as invalid format string. */
|
||||||
class format_error : public std::runtime_error {
|
class format_error : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
@ -597,30 +673,30 @@ struct char_traits<char> {
|
|||||||
// Formats a floating-point number.
|
// Formats a floating-point number.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
FMT_API static int format_float(char *buffer, std::size_t size,
|
FMT_API static int format_float(char *buffer, std::size_t size,
|
||||||
const char *format, unsigned width, int precision, T value);
|
const char *format, int precision, T value);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct char_traits<wchar_t> {
|
struct char_traits<wchar_t> {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
FMT_API static int format_float(wchar_t *buffer, std::size_t size,
|
FMT_API static int format_float(wchar_t *buffer, std::size_t size,
|
||||||
const wchar_t *format, unsigned width, int precision, T value);
|
const wchar_t *format, int precision, T value);
|
||||||
};
|
};
|
||||||
|
|
||||||
#if FMT_USE_EXTERN_TEMPLATES
|
#if FMT_USE_EXTERN_TEMPLATES
|
||||||
extern template int char_traits<char>::format_float<double>(
|
extern template int char_traits<char>::format_float<double>(
|
||||||
char *buffer, std::size_t size, const char* format, unsigned width,
|
char *buffer, std::size_t size, const char* format, int precision,
|
||||||
int precision, double value);
|
double value);
|
||||||
extern template int char_traits<char>::format_float<long double>(
|
extern template int char_traits<char>::format_float<long double>(
|
||||||
char *buffer, std::size_t size, const char* format, unsigned width,
|
char *buffer, std::size_t size, const char* format, int precision,
|
||||||
int precision, long double value);
|
long double value);
|
||||||
|
|
||||||
extern template int char_traits<wchar_t>::format_float<double>(
|
extern template int char_traits<wchar_t>::format_float<double>(
|
||||||
wchar_t *buffer, std::size_t size, const wchar_t* format, unsigned width,
|
wchar_t *buffer, std::size_t size, const wchar_t* format, int precision,
|
||||||
int precision, double value);
|
double value);
|
||||||
extern template int char_traits<wchar_t>::format_float<long double>(
|
extern template int char_traits<wchar_t>::format_float<long double>(
|
||||||
wchar_t *buffer, std::size_t size, const wchar_t* format, unsigned width,
|
wchar_t *buffer, std::size_t size, const wchar_t* format, int precision,
|
||||||
int precision, long double value);
|
long double value);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <typename Container>
|
template <typename Container>
|
||||||
@ -1982,41 +2058,42 @@ struct precision_adapter {
|
|||||||
template <typename Iterator, typename SpecHandler>
|
template <typename Iterator, typename SpecHandler>
|
||||||
FMT_CONSTEXPR Iterator parse_format_specs(Iterator it, SpecHandler &&handler) {
|
FMT_CONSTEXPR Iterator parse_format_specs(Iterator it, SpecHandler &&handler) {
|
||||||
typedef typename std::iterator_traits<Iterator>::value_type char_type;
|
typedef typename std::iterator_traits<Iterator>::value_type char_type;
|
||||||
|
char_type c = *it;
|
||||||
|
if (c == '}' || !c)
|
||||||
|
return it;
|
||||||
|
|
||||||
// Parse fill and alignment.
|
// Parse fill and alignment.
|
||||||
if (char_type c = *it) {
|
alignment align = ALIGN_DEFAULT;
|
||||||
alignment align = ALIGN_DEFAULT;
|
int i = 1;
|
||||||
int i = 1;
|
do {
|
||||||
do {
|
auto p = it + i;
|
||||||
auto p = it + i;
|
switch (*p) {
|
||||||
switch (*p) {
|
case '<':
|
||||||
case '<':
|
align = ALIGN_LEFT;
|
||||||
align = ALIGN_LEFT;
|
|
||||||
break;
|
|
||||||
case '>':
|
|
||||||
align = ALIGN_RIGHT;
|
|
||||||
break;
|
|
||||||
case '=':
|
|
||||||
align = ALIGN_NUMERIC;
|
|
||||||
break;
|
|
||||||
case '^':
|
|
||||||
align = ALIGN_CENTER;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (align != ALIGN_DEFAULT) {
|
|
||||||
handler.on_align(align);
|
|
||||||
if (p != it) {
|
|
||||||
if (c == '}') break;
|
|
||||||
if (c == '{') {
|
|
||||||
handler.on_error("invalid fill character '{'");
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
it += 2;
|
|
||||||
handler.on_fill(c);
|
|
||||||
} else ++it;
|
|
||||||
break;
|
break;
|
||||||
}
|
case '>':
|
||||||
} while (--i >= 0);
|
align = ALIGN_RIGHT;
|
||||||
}
|
break;
|
||||||
|
case '=':
|
||||||
|
align = ALIGN_NUMERIC;
|
||||||
|
break;
|
||||||
|
case '^':
|
||||||
|
align = ALIGN_CENTER;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (align != ALIGN_DEFAULT) {
|
||||||
|
if (p != it) {
|
||||||
|
if (c == '{') {
|
||||||
|
handler.on_error("invalid fill character '{'");
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
it += 2;
|
||||||
|
handler.on_fill(c);
|
||||||
|
} else ++it;
|
||||||
|
handler.on_align(align);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (--i >= 0);
|
||||||
|
|
||||||
// Parse sign.
|
// Parse sign.
|
||||||
switch (*it) {
|
switch (*it) {
|
||||||
@ -2600,6 +2677,9 @@ class basic_writer {
|
|||||||
// Formats a floating-point number (double or long double).
|
// Formats a floating-point number (double or long double).
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void write_double(T value, const format_specs &spec);
|
void write_double(T value, const format_specs &spec);
|
||||||
|
template <typename T>
|
||||||
|
void write_double_sprintf(T value, const format_specs &spec,
|
||||||
|
internal::basic_buffer<char_type>& buffer);
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
struct str_writer {
|
struct str_writer {
|
||||||
@ -2822,60 +2902,39 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
|
|||||||
return write_inf_or_nan(handler.upper ? "INF" : "inf");
|
return write_inf_or_nan(handler.upper ? "INF" : "inf");
|
||||||
|
|
||||||
basic_memory_buffer<char_type> buffer;
|
basic_memory_buffer<char_type> buffer;
|
||||||
|
if (FMT_USE_GRISU && sizeof(T) <= sizeof(double) &&
|
||||||
unsigned width = spec.width();
|
std::numeric_limits<double>::is_iec559) {
|
||||||
if (sign) {
|
internal::fp fp_value(static_cast<double>(value));
|
||||||
buffer.reserve(width > 1u ? width : 1u);
|
fp_value.normalize();
|
||||||
if (width > 0)
|
// Find a cached power of 10 close to 1 / fp_value.
|
||||||
--width;
|
int dec_exp = 0;
|
||||||
}
|
const int min_exp = -60;
|
||||||
|
auto dec_pow = internal::get_cached_power(
|
||||||
// Build format string.
|
min_exp - (fp_value.e + internal::fp::significand_size), dec_exp);
|
||||||
enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
|
internal::fp product = fp_value * dec_pow;
|
||||||
char_type format[MAX_FORMAT_SIZE];
|
// Generate output.
|
||||||
char_type *format_ptr = format;
|
internal::fp one(1ull << -product.e, product.e);
|
||||||
*format_ptr++ = '%';
|
uint32_t hi = product.f >> -one.e;
|
||||||
unsigned width_for_sprintf = width;
|
uint64_t f = product.f & (one.f - 1);
|
||||||
if (spec.flag(HASH_FLAG))
|
typedef back_insert_range<internal::basic_buffer<char_type>> range;
|
||||||
*format_ptr++ = '#';
|
basic_writer<range> w{range(buffer)};
|
||||||
width_for_sprintf = 0;
|
w.write(hi);
|
||||||
if (spec.precision() >= 0) {
|
unsigned digits = buffer.size();
|
||||||
*format_ptr++ = '.';
|
w.write('.');
|
||||||
*format_ptr++ = '*';
|
const unsigned max_digits = 18;
|
||||||
}
|
while (digits++ < max_digits) {
|
||||||
|
f *= 10;
|
||||||
append_float_length(format_ptr, value);
|
w.write(static_cast<char>('0' + (f >> -one.e)));
|
||||||
*format_ptr++ = handler.type;
|
f &= one.f - 1;
|
||||||
*format_ptr = '\0';
|
|
||||||
|
|
||||||
// Format using snprintf.
|
|
||||||
unsigned n = 0;
|
|
||||||
char_type *start = FMT_NULL;
|
|
||||||
for (;;) {
|
|
||||||
std::size_t buffer_size = buffer.capacity();
|
|
||||||
#if FMT_MSC_VER
|
|
||||||
// MSVC's vsnprintf_s doesn't work with zero size, so reserve
|
|
||||||
// space for at least one extra character to make the size non-zero.
|
|
||||||
// Note that the buffer's capacity may increase by more than 1.
|
|
||||||
if (buffer_size == 0) {
|
|
||||||
buffer.reserve(1);
|
|
||||||
buffer_size = buffer.capacity();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
start = &buffer[0];
|
|
||||||
int result = internal::char_traits<char_type>::format_float(
|
|
||||||
start, buffer_size, format, width_for_sprintf, spec.precision(), value);
|
|
||||||
if (result >= 0) {
|
|
||||||
n = internal::to_unsigned(result);
|
|
||||||
if (n < buffer.capacity())
|
|
||||||
break; // The buffer is large enough - continue with formatting.
|
|
||||||
buffer.reserve(n + 1);
|
|
||||||
} else {
|
|
||||||
// If result is negative we ask to increase the capacity by at least 1,
|
|
||||||
// but as std::vector, the buffer grows exponentially.
|
|
||||||
buffer.reserve(buffer.capacity() + 1);
|
|
||||||
}
|
}
|
||||||
|
w.write('e');
|
||||||
|
w.write(-dec_exp);
|
||||||
|
} else {
|
||||||
|
format_specs normalized_spec(spec);
|
||||||
|
normalized_spec.type_ = handler.type;
|
||||||
|
write_double_sprintf(value, normalized_spec, buffer);
|
||||||
}
|
}
|
||||||
|
unsigned n = buffer.size();
|
||||||
align_spec as = spec;
|
align_spec as = spec;
|
||||||
if (spec.align() == ALIGN_NUMERIC) {
|
if (spec.align() == ALIGN_NUMERIC) {
|
||||||
if (sign) {
|
if (sign) {
|
||||||
@ -2894,37 +2953,51 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
|
|||||||
write_padded(n, as, double_writer{n, sign, buffer});
|
write_padded(n, as, double_writer{n, sign, buffer});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename T = typename OutputIt::value_type>
|
template <typename Range>
|
||||||
class output_range {
|
template <typename T>
|
||||||
private:
|
void basic_writer<Range>::write_double_sprintf(
|
||||||
OutputIt it_;
|
T value, const format_specs &spec,
|
||||||
|
internal::basic_buffer<char_type>& buffer) {
|
||||||
|
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
|
||||||
|
FMT_ASSERT(buffer.capacity() != 0, "empty buffer");
|
||||||
|
|
||||||
// Unused yet.
|
// Build format string.
|
||||||
typedef void sentinel;
|
enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
|
||||||
sentinel end() const;
|
char_type format[MAX_FORMAT_SIZE];
|
||||||
|
char_type *format_ptr = format;
|
||||||
|
*format_ptr++ = '%';
|
||||||
|
if (spec.flag(HASH_FLAG))
|
||||||
|
*format_ptr++ = '#';
|
||||||
|
if (spec.precision() >= 0) {
|
||||||
|
*format_ptr++ = '.';
|
||||||
|
*format_ptr++ = '*';
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
append_float_length(format_ptr, value);
|
||||||
typedef OutputIt iterator;
|
*format_ptr++ = spec.type();
|
||||||
typedef T value_type;
|
*format_ptr = '\0';
|
||||||
|
|
||||||
explicit output_range(OutputIt it): it_(it) {}
|
// Format using snprintf.
|
||||||
OutputIt begin() const { return it_; }
|
char_type *start = FMT_NULL;
|
||||||
};
|
for (;;) {
|
||||||
|
std::size_t buffer_size = buffer.capacity();
|
||||||
// A range where begin() returns back_insert_iterator.
|
start = &buffer[0];
|
||||||
template <typename Container>
|
int result = internal::char_traits<char_type>::format_float(
|
||||||
class back_insert_range:
|
start, buffer_size, format, spec.precision(), value);
|
||||||
public output_range<std::back_insert_iterator<Container>> {
|
if (result >= 0) {
|
||||||
typedef output_range<std::back_insert_iterator<Container>> base;
|
unsigned n = internal::to_unsigned(result);
|
||||||
public:
|
if (n < buffer.capacity()) {
|
||||||
typedef typename Container::value_type value_type;
|
buffer.resize(n);
|
||||||
|
break; // The buffer is large enough - continue with formatting.
|
||||||
back_insert_range(Container &c): base(std::back_inserter(c)) {}
|
}
|
||||||
back_insert_range(typename base::iterator it): base(it) {}
|
buffer.reserve(n + 1);
|
||||||
};
|
} else {
|
||||||
|
// If result is negative we ask to increase the capacity by at least 1,
|
||||||
typedef basic_writer<back_insert_range<internal::buffer>> writer;
|
// but as std::vector, the buffer grows exponentially.
|
||||||
typedef basic_writer<back_insert_range<internal::wbuffer>> wwriter;
|
buffer.reserve(buffer.capacity() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Reports a system error without throwing an exception.
|
// Reports a system error without throwing an exception.
|
||||||
// Can be used to report errors from destructors.
|
// Can be used to report errors from destructors.
|
||||||
|
|||||||
@ -21,12 +21,12 @@ template void internal::arg_map<format_context>::init(
|
|||||||
const basic_format_args<format_context> &args);
|
const basic_format_args<format_context> &args);
|
||||||
|
|
||||||
template FMT_API int internal::char_traits<char>::format_float(
|
template FMT_API int internal::char_traits<char>::format_float(
|
||||||
char *buffer, std::size_t size, const char *format,
|
char *buffer, std::size_t size, const char *format, int precision,
|
||||||
unsigned width, int precision, double value);
|
double value);
|
||||||
|
|
||||||
template FMT_API int internal::char_traits<char>::format_float(
|
template FMT_API int internal::char_traits<char>::format_float(
|
||||||
char *buffer, std::size_t size, const char *format,
|
char *buffer, std::size_t size, const char *format, int precision,
|
||||||
unsigned width, int precision, long double value);
|
long double value);
|
||||||
|
|
||||||
// Explicit instantiations for wchar_t.
|
// Explicit instantiations for wchar_t.
|
||||||
|
|
||||||
@ -39,9 +39,9 @@ template void internal::arg_map<wformat_context>::init(
|
|||||||
|
|
||||||
template FMT_API int internal::char_traits<wchar_t>::format_float(
|
template FMT_API int internal::char_traits<wchar_t>::format_float(
|
||||||
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
||||||
unsigned width, int precision, double value);
|
int precision, double value);
|
||||||
|
|
||||||
template FMT_API int internal::char_traits<wchar_t>::format_float(
|
template FMT_API int internal::char_traits<wchar_t>::format_float(
|
||||||
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
||||||
unsigned width, int precision, long double value);
|
int precision, long double value);
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|||||||
@ -4,7 +4,7 @@ include $(CLEAR_VARS)
|
|||||||
LOCAL_MODULE := fmt_static
|
LOCAL_MODULE := fmt_static
|
||||||
LOCAL_MODULE_FILENAME := libfmt
|
LOCAL_MODULE_FILENAME := libfmt
|
||||||
|
|
||||||
LOCAL_SRC_FILES := fmt/format.cc
|
LOCAL_SRC_FILES := ../src/format.cc
|
||||||
|
|
||||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)
|
||||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
|
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
|
||||||
@ -39,12 +39,15 @@ for i, exp in enumerate(range(min_exponent, max_exponent + 1, step)):
|
|||||||
|
|
||||||
print('Significands:', end='')
|
print('Significands:', end='')
|
||||||
for i, fp in enumerate(powers):
|
for i, fp in enumerate(powers):
|
||||||
if i % 3 == 0:
|
if i % 4 == 0:
|
||||||
print(end='\n ')
|
print(end='\n ')
|
||||||
print(' {:0<#16x}ull'.format(fp.f, ), end=',')
|
print(' {:0<#16x}'.format(fp.f, ), end=',')
|
||||||
|
|
||||||
print('\n\nExponents:', end='')
|
print('\n\nExponents:', end='')
|
||||||
for i, fp in enumerate(powers):
|
for i, fp in enumerate(powers):
|
||||||
if i % 11 == 0:
|
if i % 11 == 0:
|
||||||
print(end='\n ')
|
print(end='\n ')
|
||||||
print(' {:5}'.format(fp.e), end=',')
|
print(' {:5}'.format(fp.e), end=',')
|
||||||
|
|
||||||
|
print('\n\nMax exponent difference:',
|
||||||
|
max([x.e - powers[i - 1].e for i, x in enumerate(powers)][1:]))
|
||||||
|
|||||||
@ -152,7 +152,9 @@ def update_site(env):
|
|||||||
if os.path.exists(html_dir):
|
if os.path.exists(html_dir):
|
||||||
shutil.rmtree(html_dir)
|
shutil.rmtree(html_dir)
|
||||||
include_dir = env.fmt_repo.dir
|
include_dir = env.fmt_repo.dir
|
||||||
if LooseVersion(version) >= LooseVersion('3.0.0'):
|
if LooseVersion(version) >= LooseVersion('5.0.0'):
|
||||||
|
include_dir = os.path.join(include_dir, 'include', 'fmt')
|
||||||
|
elif LooseVersion(version) >= LooseVersion('3.0.0'):
|
||||||
include_dir = os.path.join(include_dir, 'fmt')
|
include_dir = os.path.join(include_dir, 'fmt')
|
||||||
import build
|
import build
|
||||||
build.build_docs(version, doc_dir=target_doc_dir,
|
build.build_docs(version, doc_dir=target_doc_dir,
|
||||||
|
|||||||
@ -435,6 +435,7 @@ TEST(FormatterTest, Fill) {
|
|||||||
EXPECT_EQ("c****", format("{0:*<5}", 'c'));
|
EXPECT_EQ("c****", format("{0:*<5}", 'c'));
|
||||||
EXPECT_EQ("abc**", format("{0:*<5}", "abc"));
|
EXPECT_EQ("abc**", format("{0:*<5}", "abc"));
|
||||||
EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast<void*>(0xface)));
|
EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast<void*>(0xface)));
|
||||||
|
EXPECT_EQ("foo=", format("{:}=", "foo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatterTest, PlusSign) {
|
TEST(FormatterTest, PlusSign) {
|
||||||
|
|||||||
@ -882,17 +882,52 @@ TEST(UtilTest, ParseNonnegativeInt) {
|
|||||||
fmt::format_error, "number is too big");
|
fmt::format_error, "number is too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, FPSubtract) {
|
template <bool is_iec559>
|
||||||
auto r = fp(123, 1) - fp(102, 1);
|
void test_construct_from_double() {
|
||||||
EXPECT_EQ(r.f, 21u);
|
fmt::print("warning: double is not IEC559, skipping FP tests\n");
|
||||||
EXPECT_EQ(r.e, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, FPMultiply) {
|
template <>
|
||||||
auto r = fp(123ULL << 32, 4) * fp(56ULL << 32, 7);
|
void test_construct_from_double<true>() {
|
||||||
EXPECT_EQ(r.f, 123u * 56u);
|
auto v = fp(1.23);
|
||||||
EXPECT_EQ(r.e, 4 + 7 + 64);
|
EXPECT_EQ(v.f, 0x13ae147ae147aeu);
|
||||||
r = fp(123ULL << 32, 4) * fp(567ULL << 31, 8);
|
EXPECT_EQ(v.e, -52);
|
||||||
EXPECT_EQ(r.f, (123 * 567 + 1u) / 2);
|
}
|
||||||
EXPECT_EQ(r.e, 4 + 8 + 64);
|
|
||||||
|
TEST(FPTest, ConstructFromDouble) {
|
||||||
|
test_construct_from_double<std::numeric_limits<double>::is_iec559>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FPTest, Normalize) {
|
||||||
|
auto v = fp(0xbeef, 42);
|
||||||
|
v.normalize();
|
||||||
|
EXPECT_EQ(0xbeef000000000000, v.f);
|
||||||
|
EXPECT_EQ(-6, v.e);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FPTest, Subtract) {
|
||||||
|
auto v = fp(123, 1) - fp(102, 1);
|
||||||
|
EXPECT_EQ(v.f, 21u);
|
||||||
|
EXPECT_EQ(v.e, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FPTest, Multiply) {
|
||||||
|
auto v = fp(123ULL << 32, 4) * fp(56ULL << 32, 7);
|
||||||
|
EXPECT_EQ(v.f, 123u * 56u);
|
||||||
|
EXPECT_EQ(v.e, 4 + 7 + 64);
|
||||||
|
v = fp(123ULL << 32, 4) * fp(567ULL << 31, 8);
|
||||||
|
EXPECT_EQ(v.f, (123 * 567 + 1u) / 2);
|
||||||
|
EXPECT_EQ(v.e, 4 + 8 + 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FPTest, GetCachedPower) {
|
||||||
|
typedef std::numeric_limits<double> limits;
|
||||||
|
for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) {
|
||||||
|
int dec_exp = 0;
|
||||||
|
auto fp = fmt::internal::get_cached_power(exp, dec_exp);
|
||||||
|
EXPECT_LE(exp, fp.e);
|
||||||
|
int dec_exp_step = 8;
|
||||||
|
EXPECT_LE(fp.e, exp + dec_exp_step * log2(10));
|
||||||
|
EXPECT_DOUBLE_EQ(pow(10, dec_exp), ldexp(fp.f, fp.e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user