Skip to content

Commit 03145fa

Browse files
committed
Cleanup nan implementation and optimize for MSVC
1 parent 9044e5c commit 03145fa

File tree

1 file changed

+68
-89
lines changed

1 file changed

+68
-89
lines changed

include/ccmath/math/basic/impl/nan_double_impl.hpp

Lines changed: 68 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -10,112 +10,91 @@
1010

1111
#include "ccmath/internal/helpers/digit_to_int.hpp"
1212
#include "ccmath/internal/support/bits.hpp"
13-
1413
#include <cstdint>
1514
#include <limits>
1615

17-
namespace ccm::internal
16+
namespace ccm::internal::impl
1817
{
19-
namespace
20-
{
21-
namespace impl
22-
{
23-
24-
inline constexpr double nan_double_impl(const char * arg) noexcept
25-
{
26-
static_assert(std::numeric_limits<double>::is_iec559, "IEEE-754 representation required for this implementation");
27-
28-
std::uint64_t dbl_bits{0};
29-
bool has_hex_been_detected{false};
30-
31-
if (arg == nullptr)
32-
{
33-
return std::numeric_limits<double>::quiet_NaN(); // Default NaN
34-
}
3518

36-
if (arg[0] == '\0')
37-
{
38-
return std::numeric_limits<double>::quiet_NaN(); // Default NaN
39-
}
19+
constexpr double nan_double_impl(const char * arg) noexcept
20+
{
21+
static_assert(std::numeric_limits<double>::is_iec559, "IEEE-754 representation required for this implementation");
4022

41-
// NOLINTBEGIN
23+
#if defined(_MSC_VER) && !defined(__clang__)
24+
// Currently, MSVC always returns a Quiet NaN with no matter if a payload is
25+
// provided or not. This is different from GCC and Clang which do allow payloads to be set.
26+
// So if we detect we are using MSVC without Clang-CL then
27+
// we can just return NaN and not bother doing any extra work.
28+
// To properly mimic the behavior of MSVC.
29+
return std::numeric_limits<double>::quiet_NaN(); // Default NaN
30+
#endif
4231

43-
// Check for a hex prefix and if its detected, skip the prefix and set the flag.
44-
if (arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X'))
45-
{
46-
arg += 2;
47-
has_hex_been_detected = true;
48-
}
32+
std::uint64_t dbl_bits{0};
33+
bool has_hex_been_detected{false};
4934

50-
bool msvc_one_digit_patch{false};
35+
if (arg == nullptr)
36+
{
37+
return std::numeric_limits<double>::quiet_NaN(); // Default NaN
38+
}
5139

52-
#if defined(_MSC_VER) && !defined(__clang__)
53-
// For some reason when passing '1' or '0x1' with msvc will cause it to adds on an extra bit to the number.
54-
// I do not know why msvc does this, but it causes the number to be off by 1.
55-
// This is a patch to fix that issue.
40+
// NOLINTBEGIN
41+
if (arg[0] == '\0') //
42+
{
43+
return std::numeric_limits<double>::quiet_NaN(); // Default NaN
44+
}
5645

57-
// Check that the last character is 1 and no other characters have been provided other than zero.
58-
msvc_one_digit_patch = true;
59-
#endif
46+
// Check for a hex prefix and if its detected, skip the prefix and set the flag.
47+
if (arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X'))
48+
{
49+
arg += 2;
50+
has_hex_been_detected = true;
51+
}
6052

61-
if (!has_hex_been_detected)
53+
if (!has_hex_been_detected)
54+
{
55+
// Check that all of are characters are numbers. If we detect a non-number, return the default NaN.
56+
for (std::size_t i = 0; arg[i] != '\0'; ++i) // NOLINT
57+
{
58+
if (arg[i] < '0' || arg[i] > '9')
6259
{
63-
// Check that all of are characters are numbers. If we detect a non-number, return the default NaN.
64-
for (std::size_t i = 0; arg[i] != '\0'; ++i) // NOLINT
65-
{
66-
if (arg[i] < '0' || arg[i] > '9')
67-
{
68-
return std::numeric_limits<double>::quiet_NaN(); // Default NaN
69-
}
70-
}
60+
return std::numeric_limits<double>::quiet_NaN(); // Default NaN
7161
}
62+
}
63+
}
7264

73-
if (has_hex_been_detected)
74-
{
75-
// Calculate tag_value by handling wrapping for numbers larger than 8 digits
76-
for (std::size_t i = 0; arg[i] != '\0'; ++i)
77-
{
78-
dbl_bits *= 16;
79-
dbl_bits += static_cast<uint8_t>(ccm::helpers::digit_to_int(arg[i])); // Convert ASCII to numeric value
80-
if (i >= 15)
81-
{
82-
dbl_bits %= static_cast<uint64_t>(1e18); // Wrap around for numbers larger than 8 digits
83-
}
84-
}
85-
}
86-
else
65+
if (has_hex_been_detected)
66+
{
67+
// Calculate tag_value by handling wrapping for numbers larger than 8 digits
68+
for (std::size_t i = 0; arg[i] != '\0'; ++i)
69+
{
70+
dbl_bits *= 16;
71+
dbl_bits += static_cast<uint8_t>(ccm::helpers::digit_to_int(arg[i])); // Convert ASCII to numeric value
72+
if (i >= 15)
8773
{
88-
// Calculate tag_value by handling wrapping for numbers larger than 8 digits
89-
for (std::size_t i = 0; arg[i] != '\0'; ++i)
90-
{
91-
dbl_bits *= 10;
92-
dbl_bits += static_cast<uint8_t>(arg[i] - '0'); // Convert ASCII to numeric value
93-
if (i >= 15)
94-
{
95-
dbl_bits %= static_cast<uint64_t>(1e18); // Wrap around for numbers larger than 8 digits
96-
}
97-
}
74+
dbl_bits %= static_cast<uint64_t>(1e18); // Wrap around for numbers larger than 8 digits
9875
}
99-
// NOLINTEND
100-
101-
// Set the tag bits for NaN
102-
// dbl_bits |= UINT64_C(0x7FF8000000000000);
103-
dbl_bits |= ccm::support::bit_cast<std::uint64_t>(std::numeric_limits<double>::quiet_NaN());
104-
105-
// Subtract 1 bit from the number if the msvc patch is active
106-
if (msvc_one_digit_patch)
76+
}
77+
}
78+
else
79+
{
80+
// Calculate tag_value by handling wrapping for numbers larger than 8 digits
81+
for (std::size_t i = 0; arg[i] != '\0'; ++i)
82+
{
83+
dbl_bits *= 10;
84+
dbl_bits += static_cast<uint8_t>(arg[i] - '0'); // Convert ASCII to numeric value
85+
if (i >= 15)
10786
{
108-
// TODO: Make this more efficient
109-
// Currently, MSVC always returns a Quiet NaN with no additional bits set.
110-
// This feature applies no matter what the input is.
111-
return std::numeric_limits<double>::quiet_NaN(); // Default NaN
87+
dbl_bits %= static_cast<uint64_t>(1e18); // Wrap around for numbers larger than 8 digits
11288
}
89+
}
90+
}
91+
// NOLINTEND
11392

114-
// dbl_bits -= 1;
93+
// Set the tag bits for NaN
94+
// dbl_bits |= UINT64_C(0x7FF8000000000000);
95+
dbl_bits |= ccm::support::bit_cast<std::uint64_t>(std::numeric_limits<double>::quiet_NaN());
11596

116-
// Convert the uint64_t tag into a double NaN
117-
return ccm::support::bit_cast<double>(dbl_bits);
118-
}
119-
} // namespace impl
120-
} // namespace
121-
} // namespace ccm::internal
97+
// Convert the uint64_t tag into a double NaN
98+
return ccm::support::bit_cast<double>(dbl_bits);
99+
}
100+
} // namespace ccm::internal::impl

0 commit comments

Comments
 (0)