From 50ea6e04f509f01db21aead5e343cde346083334 Mon Sep 17 00:00:00 2001 From: Leonid Yuriev Date: Mon, 27 Jan 2020 20:56:36 +0300 Subject: [PATCH] update erthink to upstream. --- src/erthink/.travis.yml | 2 +- src/erthink/CMakeLists.txt | 2 +- src/erthink/NOTICE | 20 ++++ src/erthink/README.md | 10 ++ src/erthink/appveyor.yml | 34 ++++--- src/erthink/cmake/compiler.cmake | 25 ++++- src/erthink/cmake/profile.cmake | 2 +- src/erthink/cmake/testing.cmake | 30 +++++- src/erthink/cmake/utils.cmake | 2 +- src/erthink/erthink.h | 2 +- src/erthink/erthink_arch.h | 2 +- src/erthink/erthink_bswap.h | 16 ++- src/erthink/erthink_byteorder.h | 2 +- src/erthink/erthink_carryadd.h | 2 +- src/erthink/erthink_clz.h | 12 +-- src/erthink/erthink_d2a.h | 121 +++++++++++++++-------- src/erthink/erthink_defs.h | 124 ++++++++++++++---------- src/erthink/erthink_dynamic_constexpr.h | 2 +- src/erthink/erthink_endian.h | 2 +- src/erthink/erthink_ifunc.h | 22 +++-- src/erthink/erthink_intrin.h | 2 +- src/erthink/erthink_misc.h | 2 +- src/erthink/erthink_mul.h | 2 +- src/erthink/erthink_optimize4size.h | 2 +- src/erthink/erthink_optimize4speed.h | 2 +- src/erthink/erthink_rot.h | 2 +- src/erthink/erthink_short_alloc.h | 2 +- src/erthink/erthink_u2a.h | 2 +- src/erthink/test/d2a.cxx | 62 +++++++++++- src/erthink/test/ops.cxx | 2 +- src/erthink/test/short_alloc.cxx | 4 +- src/erthink/test/testing.h | 2 +- src/erthink/test/u2a.cxx | 2 +- 33 files changed, 364 insertions(+), 158 deletions(-) create mode 100644 src/erthink/NOTICE create mode 100644 src/erthink/README.md diff --git a/src/erthink/.travis.yml b/src/erthink/.travis.yml index cd128dc..fa758b1 100644 --- a/src/erthink/.travis.yml +++ b/src/erthink/.travis.yml @@ -16,7 +16,7 @@ compiler: os: - linux -# - osx + - osx script: > cmake --version && diff --git a/src/erthink/CMakeLists.txt b/src/erthink/CMakeLists.txt index 7fb3550..2aae402 100644 --- a/src/erthink/CMakeLists.txt +++ b/src/erthink/CMakeLists.txt @@ -1,4 +1,4 @@ -## Copyright (c) 2012-2019 Leonid Yuriev . +## Copyright (c) 2012-2020 Leonid Yuriev . ## ## Licensed under the Apache License, Version 2.0 (the "License"); ## you may not use this file except in compliance with the License. diff --git a/src/erthink/NOTICE b/src/erthink/NOTICE new file mode 100644 index 0000000..864c3ad --- /dev/null +++ b/src/erthink/NOTICE @@ -0,0 +1,20 @@ +Erthink header-only C++ library. + +Copyright (c) 1994-2019 Leonid Yuriev . +https://github.com/leo-yuriev/erthink + +-- Erthink C++ library Exceptions to the Apache 2.0 License -- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. diff --git a/src/erthink/README.md b/src/erthink/README.md new file mode 100644 index 0000000..4f3c02c --- /dev/null +++ b/src/erthink/README.md @@ -0,0 +1,10 @@ +Erthink C++ header-only library +=============================== + +https://abf.io/erthink/erthink + +Licensed under the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0) +with [exceptions](https://abf.io/erthink/erthink/blob/master/NOTICE) for GPL2/LGPL2. + +### The [repository now only mirrored on the Github](https://abf.io/erthink/erthink) due to illegal discriminatory restrictions for Russian Crimea and for sovereign crimeans. + diff --git a/src/erthink/appveyor.yml b/src/erthink/appveyor.yml index 2967549..9e8bd2f 100644 --- a/src/erthink/appveyor.yml +++ b/src/erthink/appveyor.yml @@ -2,14 +2,14 @@ version: 0.0.0.{build} environment: GTEST_SHUFFLE: 1 - GTEST_RUNTIME_LIMIT: 142 + GTEST_RUNTIME_LIMIT: 99 matrix: + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + CMAKE_GENERATOR: Visual Studio 16 2019 + TOOLSET: 142 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 CMAKE_GENERATOR: Visual Studio 15 2017 - TOOLSET: v141 -# - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 -# CMAKE_GENERATOR: Visual Studio 16 2019 -# TOOLSET: v142 + TOOLSET: 141 branches: except: @@ -20,38 +20,40 @@ configuration: - Release platform: -- x86 +- Win32 - x64 artifacts: - path: '_build/Testing/Temporary/*' name: test_results -install: +before_build: - git submodule sync - git fetch --tags --prune - git submodule update --init --recursive - git submodule foreach --recursive git fetch --tags --prune - cmake --version -before_build: +build_script: - ps: | Write-Output "*******************************************************************************" Write-Output "Configuration: $env:CONFIGURATION" Write-Output "Platform: $env:PLATFORM" - $generator = $env:CMAKE_GENERATOR - if ($env:PLATFORM -eq "x64") { - $generator = "$generator Win64" - } - Write-Output "Toolchain: $generator ($env:TOOLSET)" + Write-Output "Toolchain: $env:CMAKE_GENERATOR v$env:TOOLSET" Write-Output "*******************************************************************************" -build_script: -- ps: | md _build -Force | Out-Null cd _build - & cmake -G "$generator" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" .. + $generator = $env:CMAKE_GENERATOR + if ($env:TOOLSET -lt 142) { + if ($env:PLATFORM -eq "x64") { + $generator = "$generator Win64" + } + & cmake -G "$generator" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" .. + } else { + & cmake -G "$generator" -A $env:PLATFORM -DCMAKE_CONFIGURATION_TYPES="Debug;Release" .. + } if ($LastExitCode -ne 0) { throw "Exec: $ErrorMessage" } diff --git a/src/erthink/cmake/compiler.cmake b/src/erthink/cmake/compiler.cmake index dcfe923..c672ff3 100644 --- a/src/erthink/cmake/compiler.cmake +++ b/src/erthink/cmake/compiler.cmake @@ -1,4 +1,4 @@ -## Copyright (c) 2012-2019 Leonid Yuriev . +## Copyright (c) 2012-2020 Leonid Yuriev . ## ## Licensed under the Apache License, Version 2.0 (the "License"); ## you may not use this file except in compliance with the License. @@ -17,6 +17,11 @@ cmake_minimum_required(VERSION 3.8.2) cmake_policy(PUSH) cmake_policy(VERSION 3.8.2) +if (CMAKE_VERSION MATCHES ".*MSVC.*") + message(FATAL_ERROR "CMake from MSVC kit is unfit! " + "Please use the original CMake from https://cmake.org/download/") +endif() + if (NOT (CMAKE_C_COMPILER_LOADED OR CMAKE_CXX_COMPILER_LOADED)) message(FATAL_ERROR "This module required C or C++ to be enabled") endif() @@ -155,12 +160,12 @@ elseif(CMAKE_COMPILER_IS_ELBRUSC OR CMAKE_SYSTEM_PROCESSOR MATCHES "e2k.*|E2K.*| set(E2K TRUE) elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|x86_64.*|AMD64.*") set(X86_64 TRUE) -elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i686.*|i386.*|x86.*|amd64.*|AMD64.*") +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i686.*|i386.*|x86.*") set(X86_32 TRUE) +elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*|ARM64.*)") + set(AARCH64 TRUE) elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm.*|ARM.*)") set(ARM32 TRUE) -elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*)") - set(AARCH64 TRUE) elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64le.*") set(PPC64LE TRUE) elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64.*") @@ -347,6 +352,18 @@ if(CMAKE_COMPILER_IS_CLANG) (CMAKE_CLANG_LD AND CMAKE_CLANG_AR AND CMAKE_CLANG_NM AND CMAKE_CLANG_RANLIB)) set(CLANG_LTO_AVAILABLE TRUE) message(STATUS "Link-Time Optimization by CLANG/LLVM is available") + elseif(CMAKE_TOOLCHAIN_FILE AND NOT CMAKE_${CMAKE_PRIMARY_LANG}_COMPILER_VERSION VERSION_LESS 7.0) + set(CLANG_LTO_AVAILABLE TRUE) + if (NOT CMAKE_CLANG_AR) + set(CMAKE_CLANG_AR ${CMAKE_AR}) + endif() + if (NOT CMAKE_CLANG_NM) + set(CMAKE_CLANG_NM ${CMAKE_NM}) + endif() + if (NOT CMAKE_CLANG_RANLIB) + set(CMAKE_CLANG_RANLIB ${CMAKE_RANLIB }) + endif() + message(STATUS "Assume Link-Time Optimization by CLANG/LLVM is available via ${CMAKE_TOOLCHAIN_FILE}") else() set(CLANG_LTO_AVAILABLE FALSE) message(STATUS "Link-Time Optimization by CLANG/LLVM is NOT available") diff --git a/src/erthink/cmake/profile.cmake b/src/erthink/cmake/profile.cmake index aa29263..02a2ff6 100644 --- a/src/erthink/cmake/profile.cmake +++ b/src/erthink/cmake/profile.cmake @@ -1,4 +1,4 @@ -## Copyright (c) 2012-2019 Leonid Yuriev . +## Copyright (c) 2012-2020 Leonid Yuriev . ## ## Licensed under the Apache License, Version 2.0 (the "License"); ## you may not use this file except in compliance with the License. diff --git a/src/erthink/cmake/testing.cmake b/src/erthink/cmake/testing.cmake index 004a464..3bca187 100644 --- a/src/erthink/cmake/testing.cmake +++ b/src/erthink/cmake/testing.cmake @@ -1,4 +1,4 @@ -## Copyright (c) 2012-2019 Leonid Yuriev . +## Copyright (c) 2012-2020 Leonid Yuriev . ## ## Licensed under the Apache License, Version 2.0 (the "License"); ## you may not use this file except in compliance with the License. @@ -18,6 +18,15 @@ include(CTest) if(BUILD_TESTING) cmake_policy(PUSH) + cmake_policy(SET CMP0054 NEW) + if(NOT CMAKE_VERSION VERSION_LESS 3.9) + cmake_policy(SET CMP0068 NEW) + cmake_policy(SET CMP0069 NEW) + endif() + if(NOT CMAKE_VERSION VERSION_LESS 3.12) + cmake_policy(SET CMP0075 NEW) + endif() + # Expected GTest was already found and/or pointed via ${gtest_root}, # otherwise will search at ${gtest_paths} locations, if defined or default ones. find_package(GTest) @@ -95,6 +104,25 @@ if(BUILD_TESTING) endif() endif() + list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_17 local_HAS_CXX17) + list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_std_14 local_HAS_CXX14) + + if(NOT DEFINED GTEST_CXX_STANDARD) + if(DEFINED CMAKE_CXX_STANDARD) + set(GTEST_CXX_STANDARD ${CMAKE_CXX_STANDARD}) + elseif(NOT local_HAS_CXX17 LESS 0) + set(GTEST_CXX_STANDARD 17) + elseif(NOT local_HAS_CXX14 LESS 0) + set(GTEST_CXX_STANDARD 14) + else() + set(GTEST_CXX_STANDARD 11) + endif() + endif() + message(STATUS "Use C++${GTEST_CXX_STANDARD} for GoogleTest") + + target_compile_features(gtest PRIVATE "cxx_std_${GTEST_CXX_STANDARD}") + target_compile_features(gtest_main PRIVATE "cxx_std_${GTEST_CXX_STANDARD}") + if(CC_HAS_WERROR) if(MSVC) set(local_warn_no_error "/WX-") diff --git a/src/erthink/cmake/utils.cmake b/src/erthink/cmake/utils.cmake index 44aacc3..dd4afbf 100644 --- a/src/erthink/cmake/utils.cmake +++ b/src/erthink/cmake/utils.cmake @@ -1,4 +1,4 @@ -## Copyright (c) 2012-2019 Leonid Yuriev . +## Copyright (c) 2012-2020 Leonid Yuriev . ## ## Licensed under the Apache License, Version 2.0 (the "License"); ## you may not use this file except in compliance with the License. diff --git a/src/erthink/erthink.h b/src/erthink/erthink.h index ef18ec7..f5a950e 100644 --- a/src/erthink/erthink.h +++ b/src/erthink/erthink.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/erthink/erthink_arch.h b/src/erthink/erthink_arch.h index aecdce9..13c07fc 100644 --- a/src/erthink/erthink_arch.h +++ b/src/erthink/erthink_arch.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/erthink/erthink_bswap.h b/src/erthink/erthink_bswap.h index ace3d22..83c15e2 100644 --- a/src/erthink/erthink_bswap.h +++ b/src/erthink/erthink_bswap.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,6 +22,16 @@ #include "erthink_defs.h" #include "erthink_intrin.h" +#pragma push_macro("bswap16") +#pragma push_macro("bswap32") +#pragma push_macro("bswap64") + +#undef bswap16 +#undef bswap32 +#undef bswap64 + +//------------------------------------------------------------------------------ + #ifdef __cplusplus namespace erthink { #endif @@ -121,3 +131,7 @@ template <> inline constexpr_intrin int64_t bswap(int64_t v) { } } #endif + +#pragma pop_macro("bswap16") +#pragma pop_macro("bswap32") +#pragma pop_macro("bswap64") diff --git a/src/erthink/erthink_byteorder.h b/src/erthink/erthink_byteorder.h index 458348d..f5b5d74 100644 --- a/src/erthink/erthink_byteorder.h +++ b/src/erthink/erthink_byteorder.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/erthink/erthink_carryadd.h b/src/erthink/erthink_carryadd.h index 6a343ad..17565ea 100644 --- a/src/erthink/erthink_carryadd.h +++ b/src/erthink/erthink_carryadd.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/erthink/erthink_clz.h b/src/erthink/erthink_clz.h index 6febea2..e82d0aa 100644 --- a/src/erthink/erthink_clz.h +++ b/src/erthink/erthink_clz.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,7 +33,7 @@ namespace erthink { template inline constexpr int clz(T v); -static inline int fallback_clz8(uint8_t v) { +static __maybe_unused inline int fallback_clz8(uint8_t v) { static const int8_t lut[256] = { 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, @@ -44,7 +44,7 @@ static inline int fallback_clz8(uint8_t v) { return lut[v]; } -static inline int fallback_clz32(uint32_t v) { +static __maybe_unused inline int fallback_clz32(uint32_t v) { // LY: strive for branchless (SSA-optimizer must solve this) int r = 24, s = (v > 0xFFFF) << 4; v >>= s; @@ -57,7 +57,7 @@ static inline int fallback_clz32(uint32_t v) { return r + fallback_clz8(static_cast(v)); } -static inline int fallback_clz64(uint64_t v) { +static __maybe_unused inline int fallback_clz64(uint64_t v) { #ifdef ERTHINK_ARCH32 const uint32_t hi = static_cast(v >> 32); return (hi ? 0 : 32) + fallback_clz32(hi ? hi : static_cast(v)); @@ -112,8 +112,8 @@ template <> inline int clz(uint64_t v) { return fallback_clz64(v); } #endif /* compiler */ -static __always_inline int clz64(uint64_t v) { return clz(v); } +static __maybe_unused __always_inline int clz64(uint64_t v) { return clz(v); } -static __always_inline int clz32(uint32_t v) { return clz(v); } +static __maybe_unused __always_inline int clz32(uint32_t v) { return clz(v); } } // namespace erthink diff --git a/src/erthink/erthink_d2a.h b/src/erthink/erthink_d2a.h index 162872f..45bd523 100644 --- a/src/erthink/erthink_d2a.h +++ b/src/erthink/erthink_d2a.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -44,6 +44,22 @@ namespace erthink { +/* The ERTHINK_D2A_PEDANTRY_ACCURATE macro allows you to control the trade-off + * between conversion speed and accuracy: + * + * - Define it to non-zero for accurately conversion to impeccable string + * representation, which will be a nearest to the actual binary value. + * + * - Otherwise (if ERTHINK_D2A_PEDANTRY_ACCURATE undefiner or defined to zero) + * the conversion will be slightly faster and the result will also be correct + * (inverse conversion via stdtod() will give the original value). + * However, the string representation will be slightly larger than the ideal + * nearest value. */ + +#ifndef ERTHINK_D2A_PEDANTRY_ACCURATE +#define ERTHINK_D2A_PEDANTRY_ACCURATE 0 +#endif + #ifndef NAMESPACE_ERTHINK_D2A_DETAILS #define NAMESPACE_ERTHINK_D2A_DETAILS /* anonymous */ #endif /* NAMESPACE_ERTHINK_D2A_DETAILS */ @@ -110,15 +126,6 @@ enum { GRISU_EXPONENT_BIAS = IEEE754_DOUBLE_BIAS + IEEE754_DOUBLE_MANTISSA_SIZE }; -union casting_union { - double f; - int64_t i; - uint64_t u; - constexpr casting_union(double v) : f(v) {} - constexpr casting_union(uint64_t v) : u(v) {} - constexpr casting_union(int64_t v) : i(v) {} -}; - #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4820 /* FOO bytes padding added \ @@ -128,9 +135,9 @@ struct diy_fp { uint64_t f; int e; - explicit diy_fp(const casting_union &value) { - const uint64_t exp_bits = (value.u & IEEE754_DOUBLE_EXPONENT_MASK); - const uint64_t mantissa = (value.u & IEEE754_DOUBLE_MANTISSA_MASK); + explicit diy_fp(const int64_t i64) { + const uint64_t exp_bits = (i64 & IEEE754_DOUBLE_EXPONENT_MASK); + const uint64_t mantissa = (i64 & IEEE754_DOUBLE_MANTISSA_MASK); f = mantissa + (exp_bits ? IEEE754_DOUBLE_IMPLICIT_LEAD : 0u); e = static_cast(exp_bits >> IEEE754_DOUBLE_MANTISSA_SIZE) - (exp_bits ? GRISU_EXPONENT_BIAS : GRISU_EXPONENT_BIAS - 1); @@ -261,27 +268,34 @@ static diy_fp cached_power(const int in_exp2, int &out_exp10) { return diy_fp(power10_mas[index], power10_exp2[index]); } -static inline void round(char *end, uint64_t delta, uint64_t rest, - uint64_t ten_kappa, uint64_t upper) { +#if ERTHINK_D2A_PEDANTRY_ACCURATE +static __always_inline void round(char *end, uint64_t delta, uint64_t rest, + uint64_t ten_kappa, uint64_t upper) { while (rest < upper && delta - rest >= ten_kappa && (rest + ten_kappa < upper || /* closer */ upper - rest > rest + ten_kappa - upper)) { + assert(end[-1] > '0'); end[-1] -= 1; rest += ten_kappa; } } +#endif /* ERTHINK_D2A_PEDANTRY_ACCURATE */ -static inline char *make_digits(const diy_fp &v, const diy_fp &upper, - uint64_t delta, char *const buffer, - int &inout_exp10) { - const unsigned shift = unsigned(-upper.e); +static inline char *make_digits(const diy_fp &value, uint64_t delta, + char *const buffer, int &inout_exp10, + const diy_fp &baseline) { + const unsigned shift = unsigned(-value.e); const uint64_t mask = UINT64_MAX >> (64 - shift); char *ptr = buffer; - const diy_fp gap = upper - v; - - assert((upper.f >> shift) <= UINT_E9); - uint_fast32_t digit, body = static_cast(upper.f >> shift); - uint64_t tail = upper.f & mask; +#if ERTHINK_D2A_PEDANTRY_ACCURATE + const diy_fp gap = value - baseline; +#else + (void)baseline; +#endif /* ERTHINK_D2A_PEDANTRY_ACCURATE */ + + assert((value.f >> shift) <= UINT_E9); + uint_fast32_t digit, body = static_cast(value.f >> shift); + uint64_t tail = value.f & mask; int kappa = dec_digits(body); assert(kappa > 0); @@ -329,11 +343,14 @@ static inline char *make_digits(const diy_fp &v, const diy_fp &upper, case 0: digit = body; if (unlikely(tail < delta)) { - early: + early_last: *ptr++ = static_cast(digit + '0'); + early_skip: inout_exp10 += kappa; +#if ERTHINK_D2A_PEDANTRY_ACCURATE assert(kappa >= 0); round(ptr, delta, tail, dec_power(unsigned(kappa)) << shift, gap.f); +#endif /* ERTHINK_D2A_PEDANTRY_ACCURATE */ return ptr; } @@ -347,11 +364,6 @@ static inline char *make_digits(const diy_fp &v, const diy_fp &upper, tail &= mask; } } - - const uint64_t left = (static_cast(body) << shift) + tail; - if (unlikely(left < delta)) - goto early; - } while (unlikely(digit == 0)); while (true) { @@ -402,8 +414,12 @@ static inline char *make_digits(const diy_fp &v, const diy_fp &upper, } const uint64_t left = (static_cast(body) << shift) + tail; - if (unlikely(left < delta)) - goto early; + if (unlikely(left < delta)) { + if (likely(digit)) + goto early_last; + ++kappa; + goto early_skip; + } } done: @@ -418,9 +434,11 @@ static inline char *make_digits(const diy_fp &v, const diy_fp &upper, } inout_exp10 += kappa; +#if ERTHINK_D2A_PEDANTRY_ACCURATE assert(kappa >= -19 && kappa <= 0); const uint64_t unit = dec_power(unsigned(-kappa)); round(ptr, delta, tail, mask + 1, gap.f * unit); +#endif /* ERTHINK_D2A_PEDANTRY_ACCURATE */ return ptr; } @@ -432,7 +450,8 @@ static inline char *convert(diy_fp v, char *const buffer, int &out_exp10) { } const int left = clz64(v.f); -#if 0 +#if 0 /* Given the remaining optimizations, on average it does not have a \ + positive effect, although a little faster in the simplest cases. */ // LY: check to output as ordinal if (unlikely(v.e >= -52 && v.e <= left && (v.e >= 0 || (v.f << (64 + v.e)) == 0))) { uint64_t ordinal = (v.e < 0) ? v.f >> -v.e : v.f << v.e; @@ -446,33 +465,53 @@ static inline char *convert(diy_fp v, char *const buffer, int &out_exp10) { assert(v.f <= UINT64_MAX / 2 && left > 1); v.e -= left; v.f <<= left; + const diy_fp dec_factor = cached_power(v.e, out_exp10); // LY: get boundaries const int mojo = v.f >= UINT64_C(0x8000000080000000) ? left - 1 : left - 2; const uint64_t half_epsilon = UINT64_C(1) << mojo; diy_fp upper(v.f + half_epsilon, v.e); diy_fp lower(v.f - half_epsilon, v.e); - - const diy_fp dec_factor = cached_power(upper.e, out_exp10); upper.scale(dec_factor, false); lower.scale(dec_factor, true); - v = diy_fp::middle(upper, lower); --upper.f; assert(upper.f > lower.f); - return make_digits(v, upper, upper.f - lower.f - 1, buffer, out_exp10); + return make_digits(upper, upper.f - lower.f - 1, buffer, out_exp10, + diy_fp::middle(upper, lower)); +} + +double inline cast(int64_t i64) { + static_assert(sizeof(double) == 8 && sizeof(int64_t), "WTF?"); + double f64; + std::memcpy(&f64, &i64, 8); + return f64; +} + +double inline cast(uint64_t u64) { + static_assert(sizeof(double) == 8 && sizeof(uint64_t), "WTF?"); + double f64; + std::memcpy(&f64, &u64, 8); + return f64; +} + +int64_t inline cast(double f64) { + static_assert(sizeof(double) == 8 && sizeof(int64_t), "WTF?"); + int64_t i64; + std::memcpy(&i64, &f64, 8); + return i64; } } // namespace grisu static __maybe_unused char * -d2a(const grisu::casting_union &value, +d2a(const double &value, char *const buffer /* upto 23 chars for -22250738585072014e-324 */) { - assert(!std::isnan(value.f) && !std::isinf(value.f)); + assert(!std::isnan(value) && !std::isinf(value)); + const int64_t i64 = grisu::cast(value); // LY: strive for branchless (SSA-optimizer must solve this) *buffer = '-'; int exponent; - char *ptr = - grisu::convert(grisu::diy_fp(value), buffer + (value.i < 0), exponent); + char *ptr = grisu::convert(grisu::diy_fp(i64), buffer + (i64 < 0), exponent); if (exponent != 0) { const branchless_abs pair(exponent); ptr[0] = 'e'; diff --git a/src/erthink/erthink_defs.h b/src/erthink/erthink_defs.h index 9d7f123..22c5777 100644 --- a/src/erthink/erthink_defs.h +++ b/src/erthink/erthink_defs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,6 +30,7 @@ mode specified; termination on exception \ is not guaranteed. Specify /EHsc */ #endif + #if defined(__KERNEL__) || !defined(__cplusplus) || __cplusplus < 201103L #include #include @@ -39,6 +40,13 @@ #include #include #endif + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \ + defined(__BSD__) || defined(__NETBSD__) || defined(__bsdi__) || \ + defined(__DragonFly__) +#include +#endif /* BSD */ + #ifdef _MSC_VER #pragma warning(pop) #endif @@ -146,7 +154,7 @@ #elif __GNUC_PREREQ(8, 0) && defined(__cplusplus) && __cplusplus >= 201103L #define __fallthrough [[fallthrough]] #elif __GNUC_PREREQ(7, 0) -#define __fallthrough __attribute__((fallthrough)) +#define __fallthrough __attribute__((__fallthrough__)) #elif defined(__clang__) && defined(__cplusplus) && __cplusplus >= 201103L && \ __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough") #define __fallthrough [[clang::fallthrough]] @@ -238,9 +246,9 @@ //------------------------------------------------------------------------------ -#if defined(__GNUC__) || __has_attribute(format) +#if defined(__GNUC__) || __has_attribute(__format__) #define __printf_args(format_index, first_arg) \ - __attribute__((format(printf, format_index, first_arg))) + __attribute__((__format__(printf, format_index, first_arg))) #else #define __printf_args(format_index, first_arg) #endif @@ -250,8 +258,8 @@ #endif /* __thread */ #ifndef __always_inline -#if defined(__GNUC__) || __has_attribute(always_inline) -#define __always_inline __inline __attribute__((always_inline)) +#if defined(__GNUC__) || __has_attribute(__always_inline__) +#define __always_inline __inline __attribute__((__always_inline__)) #elif defined(_MSC_VER) #define __always_inline __forceinline #else @@ -260,16 +268,16 @@ #endif /* __always_inline */ #ifndef __must_check_result -#if defined(__GNUC__) || __has_attribute(warn_unused_result) -#define __must_check_result __attribute__((warn_unused_result)) +#if defined(__GNUC__) || __has_attribute(__warn_unused_result__) +#define __must_check_result __attribute__((__warn_unused_result__)) #else #define __must_check_result #endif #endif /* __must_check_result */ #ifndef __deprecated -#if defined(__GNUC__) || __has_attribute(deprecated) -#define __deprecated __attribute__((deprecated)) +#if defined(__GNUC__) || __has_attribute(__deprecated__) +#define __deprecated __attribute__((__deprecated__)) #elif defined(_MSC_VER) #define __deprecated __declspec(deprecated) #else @@ -278,8 +286,8 @@ #endif /* __deprecated */ #ifndef __noreturn -#if defined(__GNUC__) || __has_attribute(noreturn) -#define __noreturn __attribute__((noreturn)) +#if defined(__GNUC__) || __has_attribute(__noreturn__) +#define __noreturn __attribute__((__noreturn__)) #elif defined(_MSC_VER) #define __noreturn __declspec(noreturn) #else @@ -288,8 +296,14 @@ #endif /* __noreturn */ #ifndef __nothrow -#if defined(__GNUC__) || __has_attribute(nothrow) -#define __nothrow __attribute__((nothrow)) +#if defined(__cplusplus) +#if __cplusplus < 201703L +#define __nothrow throw() +#else +#define __nothrow noexcept(true) +#endif /* __cplusplus */ +#elif defined(__GNUC__) || __has_attribute(__nothrow__) +#define __nothrow __attribute__((__nothrow__)) #elif defined(_MSC_VER) && defined(__cplusplus) #define __nothrow __declspec(nothrow) #else @@ -303,8 +317,8 @@ * Such a function can be subject to common subexpression elimination * and loop optimization just as an arithmetic operator would be. * These functions should be declared with the attribute pure. */ -#if defined(__GNUC__) || __has_attribute(pure) -#define __pure_function __attribute__((pure)) +#if defined(__GNUC__) || __has_attribute(__pure__) +#define __pure_function __attribute__((__pure__)) #else #define __pure_function #endif @@ -320,8 +334,8 @@ * data pointed to must not be declared const. Likewise, a function * that calls a non-const function usually must not be const. * It does not make sense for a const function to return void. */ -#if defined(__GNUC__) || __has_attribute(const) -#define __const_function __attribute__((const)) +#if defined(__GNUC__) || __has_attribute(__const__) +#define __const_function __attribute__((__const__)) #else #define __const_function #endif @@ -329,10 +343,10 @@ #ifndef __optimize #if defined(__OPTIMIZE__) -#if defined(__clang__) && !__has_attribute(optimize) +#if defined(__clang__) && !__has_attribute(__optimize__) #define __optimize(ops) -#elif defined(__GNUC__) || __has_attribute(optimize) -#define __optimize(ops) __attribute__((optimize(ops))) +#elif defined(__GNUC__) || __has_attribute(__optimize__) +#define __optimize(ops) __attribute__((__optimize__(ops))) #else #define __optimize(ops) #endif @@ -344,12 +358,14 @@ #ifndef __hot #if defined(__OPTIMIZE__) #if defined(__e2k__) -#define __hot __attribute__((hot)) __optimize(3) -#elif defined(__clang__) && !__has_attribute(hot) +#define __hot __attribute__((__hot__)) __optimize(3) +#elif defined(__clang__) && !__has_attribute(__hot__) && \ + __has_attribute(__section__) && \ + (defined(__linux__) || defined(__gnu_linux__)) /* just put frequently used functions in separate section */ -#define __hot __attribute__((section("text.hot"))) __optimize("O3") -#elif defined(__GNUC__) || __has_attribute(hot) -#define __hot __attribute__((hot)) __optimize("O3") +#define __hot __attribute__((__section__("text.hot"))) __optimize("O3") +#elif defined(__GNUC__) || __has_attribute(__hot__) +#define __hot __attribute__((__hot__)) __optimize("O3") #else #define __hot __optimize("O3") #endif @@ -361,12 +377,14 @@ #ifndef __cold #if defined(__OPTIMIZE__) #if defined(__e2k__) -#define __cold __optimize(1) __attribute__((cold)) -#elif defined(__clang__) && !__has_attribute(cold) +#define __cold __optimize(1) __attribute__((__cold__)) +#elif defined(__clang__) && !__has_attribute(__cold__) && \ + __has_attribute(__section__) && \ + (defined(__linux__) || defined(__gnu_linux__)) /* just put infrequently used functions in separate section */ -#define __cold __attribute__((section("text.unlikely"))) __optimize("Os") -#elif defined(__GNUC__) || __has_attribute(cold) -#define __cold __attribute__((cold)) __optimize("Os") +#define __cold __attribute__((__section__("text.unlikely"))) __optimize("Os") +#elif defined(__GNUC__) || __has_attribute(__cold__) +#define __cold __attribute__((__cold__)) __optimize("Os") #else #define __cold __optimize("Os") #endif @@ -376,16 +394,16 @@ #endif /* __cold */ #ifndef __flatten -#if defined(__OPTIMIZE__) && (defined(__GNUC__) || __has_attribute(flatten)) -#define __flatten __attribute__((flatten)) +#if defined(__OPTIMIZE__) && (defined(__GNUC__) || __has_attribute(__flatten__)) +#define __flatten __attribute__((__flatten__)) #else #define __flatten #endif #endif /* __flatten */ #ifndef __noinline -#if defined(__GNUC__) || __has_attribute(noinline) -#define __noinline __attribute__((noinline)) +#if defined(__GNUC__) || __has_attribute(__noinline__) +#define __noinline __attribute__((__noinline__)) #elif defined(_MSC_VER) #define __noinline __declspec(noinline) #elif defined(__SUNPRO_C) || defined(__sun) || defined(sun) @@ -396,8 +414,8 @@ #endif /* __noinline */ #ifndef __maybe_unused -#if defined(__GNUC__) || __has_attribute(unused) -#define __maybe_unused __attribute__((unused)) +#if defined(__GNUC__) || __has_attribute(__unused__) +#define __maybe_unused __attribute__((__unused__)) #else #define __maybe_unused #endif @@ -405,13 +423,13 @@ //------------------------------------------------------------------------------ -#ifndef __dll_hidden -#if defined(__GNUC__) || __has_attribute(visibility) -#define __hidden __attribute__((visibility("hidden"))) +#ifndef __hidden +#if defined(__GNUC__) || __has_attribute(__visibility__) +#define __hidden __attribute__((__visibility__("hidden"))) #else #define __hidden #endif -#endif /* __dll_hidden */ +#endif /* __hidden */ #ifndef __dll_export #if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) @@ -420,8 +438,8 @@ #else #define __dll_export __declspec(dllexport) #endif -#elif defined(__GNUC__) || __has_attribute(visibility) -#define __dll_export __attribute__((visibility("default"))) +#elif defined(__GNUC__) || __has_attribute(__visibility__) +#define __dll_export __attribute__((__visibility__("default"))) #else #define __dll_export #endif @@ -434,16 +452,16 @@ #else #define __dll_import __declspec(dllimport) #endif -#elif defined(__GNUC__) || __has_attribute(visibility) -#define __dll_import __attribute__((visibility("default"))) +#elif defined(__GNUC__) || __has_attribute(__visibility__) +#define __dll_import __attribute__((__visibility__("default"))) #else #define __dll_import #endif #endif /* __dll_import */ #ifndef __dll_visibility_default -#if defined(__GNUC__) || __has_attribute(visibility) -#define __dll_visibility_default __attribute__((visibility("default"))) +#if defined(__GNUC__) || __has_attribute(__visibility__) +#define __dll_visibility_default __attribute__((__visibility__("default"))) #else #define __dll_visibility_default #endif @@ -474,8 +492,8 @@ static __inline void __noop_consume_args(void *anchor, ...) { (void)anchor; } #ifdef _MSC_VER #define ERTHINK_PACKED_STRUCT(name) \ __pragma(pack(push, 1)) struct name __pragma(pack(pop)) -#elif defined(__GNUC__) || __has_attribute(packed) -#define ERTHINK_PACKED_STRUCT(name) struct __attribute__((packed)) name +#elif defined(__GNUC__) || __has_attribute(__packed__) +#define ERTHINK_PACKED_STRUCT(name) struct __attribute__((__packed__)) name #else #error Unsupported C/C++ compiler #endif /* FPT_PACKED_STRUCT */ @@ -518,8 +536,8 @@ typedef _Complex float __cfloat128 __attribute__((__mode__(__TC__))); #endif /* Workaround for Coverity Scan */ #if !defined(alignas) && (!defined(__cplusplus) || __cplusplus < 201103L) -#if defined(__GNUC__) || defined(__clang__) || __has_attribute(aligned) -#define alignas(N) __attribute__((aligned(N))) +#if defined(__GNUC__) || defined(__clang__) || __has_attribute(__aligned__) +#define alignas(N) __attribute__((__aligned__(N))) #elif defined(_MSC_VER) #define alignas(N) __declspec(align(N)) #else @@ -556,7 +574,8 @@ typedef _Complex float __cfloat128 __attribute__((__mode__(__TC__))); //------------------------------------------------------------------------------ -#if defined(__cplusplus) && !defined(DEFINE_ENUM_FLAG_OPERATORS) +#ifndef DEFINE_ENUM_FLAG_OPERATORS +#if defined(__cplusplus) // Define operator overloads to enable bit operations on enum values that are // used to define flags (based on Microsoft's DEFINE_ENUM_FLAG_OPERATORS). #define DEFINE_ENUM_FLAG_OPERATORS(ENUM) \ @@ -584,3 +603,4 @@ typedef _Complex float __cfloat128 __attribute__((__mode__(__TC__))); #else /* __cplusplus */ #define DEFINE_ENUM_FLAG_OPERATORS(ENUM) /* nope, C allows these operators */ #endif /* !__cplusplus */ +#endif /* DEFINE_ENUM_FLAG_OPERATORS */ diff --git a/src/erthink/erthink_dynamic_constexpr.h b/src/erthink/erthink_dynamic_constexpr.h index a547d3b..72a0613 100644 --- a/src/erthink/erthink_dynamic_constexpr.h +++ b/src/erthink/erthink_dynamic_constexpr.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/erthink/erthink_endian.h b/src/erthink/erthink_endian.h index 976a369..a66b920 100644 --- a/src/erthink/erthink_endian.h +++ b/src/erthink/erthink_endian.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/erthink/erthink_ifunc.h b/src/erthink/erthink_ifunc.h index a77e1c3..abb3025 100644 --- a/src/erthink/erthink_ifunc.h +++ b/src/erthink/erthink_ifunc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,7 +25,7 @@ * and https://sourceware.org/glibc/wiki/GNU_IFUNC */ #ifndef ERTHINK_USE_ELF_IFUNC -#if __has_attribute(ifunc) && \ +#if __has_attribute(__ifunc__) && \ defined(__ELF__) /* ifunc is broken on Darwin/OSX */ /* Use ifunc/gnu_indirect_function if corresponding attribute is available, * Assuming compiler will generate properly code even when @@ -51,7 +51,7 @@ RESOLVER) \ __extern_C API_VISIBILITY RESULT_TYPE NAME DECLARGS_PARENTHESIZED; -#if __has_attribute(ifunc) || \ +#if __has_attribute(__ifunc__) || \ (defined(__ELF__) && __GLIBC_PREREQ(2, 11) && __GNUC_PREREQ(4, 6)) #define ERTHINK_DEFINE_IFUNC(API_VISIBILITY, RESULT_TYPE, NAME, \ @@ -59,10 +59,11 @@ RESOLVER) \ \ ERTHINK_IFUNC_RESOLVER_API(API_VISIBILITY) \ - __attribute__((used)) RESULT_TYPE(*RESOLVER(void)) DECLARGS_PARENTHESIZED; \ + __attribute__((__used__)) RESULT_TYPE(*RESOLVER(void)) \ + DECLARGS_PARENTHESIZED; \ \ __extern_C API_VISIBILITY RESULT_TYPE NAME DECLARGS_PARENTHESIZED \ - __attribute__((ifunc(STRINGIFY(RESOLVER)))); + __attribute__((__ifunc__(STRINGIFY(RESOLVER)))); #else @@ -81,7 +82,7 @@ /* *INDENT-ON* */ /* clang-format on */ -#endif /* __has_attribute(ifunc) */ +#endif /* __has_attribute(__ifunc__) */ #else /* ERTHINK_USE_ELF_IFUNC */ #define ERTHINK_IFUNC_RESOLVER_API(API) static @@ -121,7 +122,7 @@ return NAME##_iFuncPtr CALLARGS_PARENTHESIZED; \ } -#if __GNUC_PREREQ(4, 0) || __has_attribute(constructor) +#if __GNUC_PREREQ(4, 0) || __has_attribute(__constructor__) #define ERTHINK_DEFINE_IFUNC(API_VISIBILITY, RESULT_TYPE, NAME, \ DECLARGS_PARENTHESIZED, CALLARGS_PARENTHESIZED, \ @@ -132,11 +133,12 @@ ERTHINK_IFUNC_RESOLVER_API(API_VISIBILITY) \ RESULT_TYPE(*RESOLVER(void)) DECLARGS_PARENTHESIZED; \ \ - static __cold void __attribute__((constructor)) NAME##_iFunc_init(void) { \ + static __cold void __attribute__((__constructor__)) \ + NAME##_iFunc_init(void) { \ NAME##_iFuncPtr = RESOLVER(); \ } -#else /* __has_attribute(constructor) */ +#else /* __has_attribute(__constructor__) */ #define ERTHINK_DEFINE_IFUNC(API_VISIBILITY, RESULT_TYPE, NAME, \ DECLARGS_PARENTHESIZED, CALLARGS_PARENTHESIZED, \ @@ -152,7 +154,7 @@ \ RESULT_TYPE(*NAME##_iFuncPtr) DECLARGS_PARENTHESIZED = NAME##_proxy; -#endif /* __has_attribute(constructor) */ +#endif /* __has_attribute(__constructor__) */ #endif /* __cplusplus */ diff --git a/src/erthink/erthink_intrin.h b/src/erthink/erthink_intrin.h index f8d149e..9ff21e3 100644 --- a/src/erthink/erthink_intrin.h +++ b/src/erthink/erthink_intrin.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/erthink/erthink_misc.h b/src/erthink/erthink_misc.h index 5d27bc9..00bdc38 100644 --- a/src/erthink/erthink_misc.h +++ b/src/erthink/erthink_misc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/erthink/erthink_mul.h b/src/erthink/erthink_mul.h index b7e3447..97fc5b2 100644 --- a/src/erthink/erthink_mul.h +++ b/src/erthink/erthink_mul.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/erthink/erthink_optimize4size.h b/src/erthink/erthink_optimize4size.h index 385eee5..7207835 100644 --- a/src/erthink/erthink_optimize4size.h +++ b/src/erthink/erthink_optimize4size.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/erthink/erthink_optimize4speed.h b/src/erthink/erthink_optimize4speed.h index 1bde58d..9d8ea35 100644 --- a/src/erthink/erthink_optimize4speed.h +++ b/src/erthink/erthink_optimize4speed.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/erthink/erthink_rot.h b/src/erthink/erthink_rot.h index 5655307..0a4f97c 100644 --- a/src/erthink/erthink_rot.h +++ b/src/erthink/erthink_rot.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/erthink/erthink_short_alloc.h b/src/erthink/erthink_short_alloc.h index 310132c..73480f8 100644 --- a/src/erthink/erthink_short_alloc.h +++ b/src/erthink/erthink_short_alloc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Leonid Yuriev . + * Copyright (c) 2019-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/erthink/erthink_u2a.h b/src/erthink/erthink_u2a.h index 9dfd441..215c7df 100644 --- a/src/erthink/erthink_u2a.h +++ b/src/erthink/erthink_u2a.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/erthink/test/d2a.cxx b/src/erthink/test/d2a.cxx index 6ed0de7..6c38717 100644 --- a/src/erthink/test/d2a.cxx +++ b/src/erthink/test/d2a.cxx @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -39,6 +39,35 @@ __hot __dll_export __noinline char *_d2a(const double value, char *ptr) { //------------------------------------------------------------------------------ +// #define SHOW_LAST_DIGIT_ROUNDING_INACCURACY +#ifdef SHOW_LAST_DIGIT_ROUNDING_INACCURACY +static std::tuple mantissa_str_diff(const char *a, + const char *b) { + int i = 0, j = 0; + for (;;) { + if (a[i] == '.') + i++; + const bool a_end = (a[i] == 'e' || a[i] == 'E' || a[i] == '\0'); + + if (b[j] == '.') + j++; + const bool b_end = (b[j] == 'e' || b[j] == 'E' || b[j] == '\0'); + + if (a_end && b_end) + break; + + const char a_digit = a_end ? '0' : a[i]; + const char b_digit = b_end ? '0' : b[j]; + if (a_digit != b_digit) + return std::make_tuple(true, i, j); + + i += !a_end; + j += !b_end; + } + return std::make_tuple(false, 0, 0); +} +#endif /* SHOW_LAST_DIGIT_ROUNDING_INACCURACY */ + static void probe_d2a(char (&buffer)[23 + 1], const double value) { char *d2a_end = _d2a(value, buffer); ASSERT_LT(buffer, d2a_end); @@ -49,6 +78,29 @@ static void probe_d2a(char (&buffer)[23 + 1], const double value) { double probe = strtod(buffer, &strtod_end); EXPECT_EQ(d2a_end, strtod_end); EXPECT_EQ(value, probe); + +#ifdef SHOW_LAST_DIGIT_ROUNDING_INACCURACY + int i = 0; + const char *s = buffer; + for (;;) { + if (*s == '-' || *s == '.') + s++; + else if (s[i] >= '0' && s[i] <= '9') + i++; + else + break; + } + char print_buffer[32]; + snprintf(print_buffer, sizeof(print_buffer), "%.*e", i - 1, value); + const auto diff = mantissa_str_diff(buffer, print_buffer); + if (std::get<0>(diff)) { + printf("d2a:%s <> printf:%s\n" + "%*c%*c\n", + buffer, print_buffer, std::get<1>(diff) + 5, '^', + std::get<2>(diff) + 16, '^'); + fflush(nullptr); + } +#endif /* SHOW_LAST_DIGIT_ROUNDING_INACCURACY */ } TEST(d2a, trivia) { @@ -94,16 +146,16 @@ TEST(d2a, trivia) { } static bool probe_d2a(uint64_t u64, char (&buffer)[23 + 1]) { - erthink::grisu::casting_union casting(u64); - switch (std::fpclassify(casting.f)) { + const double f64 = erthink::grisu::cast(u64); + switch (std::fpclassify(f64)) { case FP_NAN: case FP_INFINITE: return false; default: - probe_d2a(buffer, casting.f); + probe_d2a(buffer, f64); } - const float f32 = static_cast(casting.f); + const float f32 = static_cast(f64); switch (std::fpclassify(f32)) { case FP_NAN: case FP_INFINITE: diff --git a/src/erthink/test/ops.cxx b/src/erthink/test/ops.cxx index 336f1f2..d3d0cd5 100644 --- a/src/erthink/test/ops.cxx +++ b/src/erthink/test/ops.cxx @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/erthink/test/short_alloc.cxx b/src/erthink/test/short_alloc.cxx index f93cabb..df0e62c 100644 --- a/src/erthink/test/short_alloc.cxx +++ b/src/erthink/test/short_alloc.cxx @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,6 +23,7 @@ #include #endif +#include #include #include #include @@ -45,6 +46,7 @@ typedef ::testing::Types< template class ShortAlloc : public ::testing::Test {}; #ifdef TYPED_TEST_SUITE_P TYPED_TEST_SUITE_P(ShortAlloc); +TYPED_TEST_SUITE(ShortAlloc, Sizes); #else TYPED_TEST_CASE_P(ShortAlloc); TYPED_TEST_CASE(ShortAlloc, Sizes); diff --git a/src/erthink/test/testing.h b/src/erthink/test/testing.h index db3e90f..ff79ec0 100644 --- a/src/erthink/test/testing.h +++ b/src/erthink/test/testing.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/src/erthink/test/u2a.cxx b/src/erthink/test/u2a.cxx index 62538d8..1ea5fcd 100644 --- a/src/erthink/test/u2a.cxx +++ b/src/erthink/test/u2a.cxx @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994-2019 Leonid Yuriev . + * Copyright (c) 1994-2020 Leonid Yuriev . * https://github.com/leo-yuriev/erthink * * Licensed under the Apache License, Version 2.0 (the "License");