jk-jeon / grisu-exact Goto Github PK
View Code? Open in Web Editor NEWReference implementation of Grisu-Exact in C++
License: Apache License 2.0
Reference implementation of Grisu-Exact in C++
License: Apache License 2.0
I've made some tests with MSVC and got a crash on ADCX instruction. My CPU predates Intel ADX (introduced in Broadwell). But it seems like ADC can be used instead.
diff --git a/grisu_exact.h b/grisu_exact.h
index 4d99f8a89..d8b69fcea 100644
--- a/grisu_exact.h
+++ b/grisu_exact.h
@@ -145,8 +145,8 @@ namespace jkj {
#if defined(_MSC_VER) && defined(_M_X64)
std::uint64_t high, low;
- auto carry = _addcarryx_u64(0, g0.low(), g10, &low);
- _addcarryx_u64(carry, g0.high(), 0, &high);
+ auto carry = _addcarry_u64(0, g0.low(), g10, &low);
+ _addcarry_u64(carry, g0.high(), 0, &high);
return high;
#elif (defined(__GNUC__) || defined(__clang__)) && defined(__SIZEOF_INT128__) && defined(__x86_64__)
return uint128{ g0.internal_ + g10 }.high();
There are two small problems when trying to build with gcc -m32
. The diff below fixes both.
diff --git a/grisu_exact.h b/grisu_exact.h
index eb7801e7e..4d99f8a89 100644
--- a/grisu_exact.h
+++ b/grisu_exact.h
@@ -53,7 +53,7 @@ namespace jkj {
struct uint128 {
uint128() = default;
-#if defined(__GNUC__) || defined(__clang__) && defined(__SIZEOF_INT128__) && defined(__x86_64__)
+#if (defined(__GNUC__) || defined(__clang__)) && defined(__SIZEOF_INT128__) && defined(__x86_64__)
unsigned __int128 internal_;
constexpr uint128(std::uint64_t high, std::uint64_t low) noexcept :
@@ -204,7 +204,7 @@ namespace jkj {
if (exp >= int(sizeof(UInt) * 8)) {
return false;
}
- return f == ((f >> exp) << exp);
+ return x == ((x >> exp) << exp);
#endif
}
Otherwise, this is great work, it's very easy to use and it works very well, thank you :)
Sorry for the spam, I think it'll be all after this one...
There are two sorts of problems when trying to build with Clang/MSVC (clang-cl) on Windows.
First, in some cases, you test for _MSC_VER (and _M_X64) first, and sometimes for Clang/GCC (and 64-bit) first. Problem is, with Clang/MSVC, both code paths match because it defines __clang__
, _MSC_VER
and _M_X64
. To make sure Clang uses the GCC/Clang code paths, they should come first. I've done that in the patch below.
More importantly, for some reason, Clang/MSVC has problems on line 1743 with the definition of power_of_10
. It does not like the auto return type there. Replacing auto with extended_significand_type fixes the problem. This change is in the patch below, at the end. I don't know why that is, and I use a very C-with-classes kind of C++ in my own code so I won't try to speculate on this kind of modern C++ thing.
This patch comes after the one I posted in #10.
diff --git a/grisu_exact.h b/grisu_exact.h
index d8b69fcea..08737036f 100644
--- a/grisu_exact.h
+++ b/grisu_exact.h
@@ -85,12 +85,12 @@ namespace jkj {
JKJ_GRISU_EXACT_SAFEBUFFERS
inline uint128 umul128(std::uint64_t x, std::uint64_t y) noexcept {
-#if defined(_MSC_VER) && defined(_M_X64)
+#if (defined(__GNUC__) || defined(__clang__)) && defined(__SIZEOF_INT128__) && defined(__x86_64__)
+ return (unsigned __int128)(x) * (unsigned __int128)(y);
+#elif defined(_MSC_VER) && defined(_M_X64)
uint128 result;
result.low_ = _umul128(x, y, &result.high_);
return result;
-#elif (defined(__GNUC__) || defined(__clang__)) && defined(__SIZEOF_INT128__) && defined(__x86_64__)
- return (unsigned __int128)(x) * (unsigned __int128)(y);
#else
constexpr auto mask = (std::uint64_t(1) << 32) - std::uint64_t(1);
@@ -113,11 +113,11 @@ namespace jkj {
JKJ_GRISU_EXACT_SAFEBUFFERS
inline std::uint64_t umul128_upper64(std::uint64_t x, std::uint64_t y) noexcept {
-#if defined(_MSC_VER) && defined(_M_X64)
- return __umulh(x, y);
-#elif (defined(__GNUC__) || defined(__clang__)) && defined(__SIZEOF_INT128__) && defined(__x86_64__)
+#if (defined(__GNUC__) || defined(__clang__)) && defined(__SIZEOF_INT128__) && defined(__x86_64__)
auto p = (unsigned __int128)(x) * (unsigned __int128)(y);
return std::uint64_t(p >> 64);
+#elif defined(_MSC_VER) && defined(_M_X64)
+ return __umulh(x, y);
#else
constexpr auto mask = (std::uint64_t(1) << 32) - std::uint64_t(1);
@@ -143,13 +143,13 @@ namespace jkj {
auto g0 = umul128(x, y.high());
auto g10 = umul128_upper64(x, y.low());
-#if defined(_MSC_VER) && defined(_M_X64)
+#if (defined(__GNUC__) || defined(__clang__)) && defined(__SIZEOF_INT128__) && defined(__x86_64__)
+ return uint128{ g0.internal_ + g10 }.high();
+#elif defined(_MSC_VER) && defined(_M_X64)
std::uint64_t high, low;
auto carry = _addcarry_u64(0, g0.low(), g10, &low);
_addcarry_u64(carry, g0.high(), 0, &high);
return high;
-#elif (defined(__GNUC__) || defined(__clang__)) && defined(__SIZEOF_INT128__) && defined(__x86_64__)
- return uint128{ g0.internal_ + g10 }.high();
#else
auto intermediate = g0.low() + g10;
return g0.high() + (intermediate < g10);
@@ -178,16 +178,7 @@ namespace jkj {
static_assert(std::is_same_v<UInt, std::uint32_t> || std::is_same_v<UInt, std::uint64_t>);
assert(exp >= 1);
assert(x != 0);
-#if defined(_MSC_VER) && defined(_M_X64)
- unsigned long index;
- if constexpr (std::is_same_v<UInt, std::uint32_t>) {
- _BitScanForward(&index, x);
- }
- else {
- _BitScanForward64(&index, x);
- }
- return int(index) >= exp;
-#elif (defined(__GNUC__) || defined(__clang__)) && defined(__x86_64__)
+#if (defined(__GNUC__) || defined(__clang__)) && defined(__x86_64__)
int index;
if constexpr (std::is_same_v<UInt, std::uint32_t>) {
static_assert(sizeof(unsigned long) == 4,
@@ -200,6 +191,15 @@ namespace jkj {
index = __builtin_ctzll((unsigned long long)x);
}
return index >= exp;
+#elif defined(_MSC_VER) && defined(_M_X64)
+ unsigned long index;
+ if constexpr (std::is_same_v<UInt, std::uint32_t>) {
+ _BitScanForward(&index, x);
+ }
+ else {
+ _BitScanForward64(&index, x);
+ }
+ return int(index) >= exp;
#else
if (exp >= int(sizeof(UInt) * 8)) {
return false;
@@ -1748,8 +1748,9 @@ namespace jkj {
using common_info<Float>::integer_check_exponent_upper_bound_for_p_p2;
using common_info<Float>::integer_check_exponent_upper_bound_for_p_p1;
+ // Clang/MSVC (clang-cl) fails when return type is set to auto for some reason
template <unsigned int e>
- static constexpr auto power_of_10 = compute_power(extended_significand_type(10), e);
+ static constexpr extended_significand_type power_of_10 = compute_power(extended_significand_type(10), e);
//// The main algorithm assumes the input is a normal/subnormal finite number
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.