Skip to content

Commit

Permalink
Cleanup nan implementation and add docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Rinzii committed Apr 6, 2024
1 parent c0a239c commit 58b5b70
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 61 deletions.
5 changes: 4 additions & 1 deletion include/ccmath/math/basic/impl/nan_double_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ namespace ccm::internal::impl

constexpr double nan_double_impl(const char * arg) noexcept
{
static_assert(std::numeric_limits<double>::is_iec559, "IEEE-754 representation required for this implementation");
if constexpr (!std::numeric_limits<double>::is_iec559)
{
return 0.0;
}

#if defined(_MSC_VER) && !defined(__clang__)
// Currently, MSVC always returns a Quiet NaN no matter if a payload is
Expand Down
117 changes: 61 additions & 56 deletions include/ccmath/math/basic/impl/nan_float_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,77 +14,82 @@
#include <cstdint>
#include <limits>

namespace ccm::internal
namespace ccm::internal::impl
{
namespace
constexpr float nan_float_impl(const char * arg) noexcept
{
namespace impl
if constexpr (!std::numeric_limits<float>::is_iec559)
{
return 0.0;
}

inline constexpr float nan_float_impl(const char * arg) noexcept
{
static_assert(std::numeric_limits<float>::is_iec559, "IEEE-754 representation required for this implementation");
#if defined(_MSC_VER) && !defined(__clang__)
// Currently, MSVC always returns a Quiet NaN no matter if a payload is
// provided or not. This is different from GCC and Clang which do allow payloads to be set.
// So if we detect we are using MSVC without Clang-CL then
// we can just return NaN and not bother doing any extra work.
// To properly mimic the behavior of MSVC.
return std::numeric_limits<double>::quiet_NaN(); // Default NaN
#endif

std::uint32_t flt_bits{0};
bool has_hex_been_detected{false};
std::uint32_t flt_bits{0};
bool has_hex_been_detected{false};

// NOLINTBEGIN
// NOLINTBEGIN

// Check for a hex prefix and if its detected, skip the prefix and set the flag.
if (arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X'))
{
arg += 2;
has_hex_been_detected = true;
}
// Check for a hex prefix and if its detected, skip the prefix and set the flag.
if (arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X'))
{
arg += 2;
has_hex_been_detected = true;
}

if (!has_hex_been_detected)
if (!has_hex_been_detected)
{
// Check that all of are characters are numbers. If we detect a non-number, return the default NaN.
for (std::size_t i = 0; arg[i] != '\0'; ++i) // NOLINT
{
if (arg[i] < '0' || arg[i] > '9')
{
// Check that all of are characters are numbers. If we detect a non-number, return the default NaN.
for (std::size_t i = 0; arg[i] != '\0'; ++i) // NOLINT
{
if (arg[i] < '0' || arg[i] > '9')
{
return std::numeric_limits<float>::quiet_NaN(); // Default NaN
}
}
return std::numeric_limits<float>::quiet_NaN(); // Default NaN
}
}
}

if (has_hex_been_detected)
if (has_hex_been_detected)
{
// Calculate tag_value by handling wrapping for numbers larger than 8 digits
for (std::size_t i = 0; arg[i] != '\0'; ++i)
{
flt_bits *= 16;
flt_bits += static_cast<uint8_t>(ccm::helpers::digit_to_int(arg[i])); // Convert ASCII to numeric value
if (i >= 7)
{
// Calculate tag_value by handling wrapping for numbers larger than 8 digits
for (std::size_t i = 0; arg[i] != '\0'; ++i)
{
flt_bits *= 16;
flt_bits += static_cast<uint8_t>(ccm::helpers::digit_to_int(arg[i])); // Convert ASCII to numeric value
if (i >= 7)
{
flt_bits %= static_cast<uint32_t>(1e8); // Wrap around for numbers larger than 8 digits
}
}
flt_bits %= static_cast<uint32_t>(1e8); // Wrap around for numbers larger than 8 digits
}
else
}
}
else
{
// Calculate tag_value by handling wrapping for numbers larger than 8 digits
for (std::size_t i = 0; arg[i] != '\0'; ++i)
{
flt_bits *= 10;
flt_bits += static_cast<uint8_t>(ccm::helpers::digit_to_int(arg[i])); // Convert ASCII to numeric value
if (i >= 7)
{
// Calculate tag_value by handling wrapping for numbers larger than 8 digits
for (std::size_t i = 0; arg[i] != '\0'; ++i)
{
flt_bits *= 10;
flt_bits += static_cast<uint8_t>(ccm::helpers::digit_to_int(arg[i])); // Convert ASCII to numeric value
if (i >= 7)
{
flt_bits %= static_cast<uint32_t>(1e8); // Wrap around for numbers larger than 8 digits
}
}
flt_bits %= static_cast<uint32_t>(1e8); // Wrap around for numbers larger than 8 digits
}
}
}

// NOLINTEND
// NOLINTEND

// Set the tag bits for NaN
// flt_bits |= UINT32_C(0x7F800000);
flt_bits |= ccm::support::bit_cast<std::uint32_t>(std::numeric_limits<float>::quiet_NaN());
// Set the tag bits for NaN
// flt_bits |= UINT32_C(0x7F800000);
flt_bits |= ccm::support::bit_cast<std::uint32_t>(std::numeric_limits<float>::quiet_NaN());

// Convert the bits to a float
return ccm::support::bit_cast<float>(flt_bits);
}
} // namespace impl
} // namespace
} // namespace ccm::internal
// Convert the bits to a float
return ccm::support::bit_cast<float>(flt_bits);
}
} // namespace ccm::internal::impl
24 changes: 20 additions & 4 deletions include/ccmath/math/basic/nan.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,38 @@
namespace ccm
{
/**
* @brief
* @param arg
* @return
* @brief not-a-number (NaN) function
* @param arg Narrow character string identifying the contents of a NaN
* @return The quiet NaN value that corresponds to the identifying string arg or zero if the implementation does not support quiet NaNs.
*/
constexpr double nan(const char * arg) noexcept
{
return ccm::internal::impl::nan_double_impl(arg);
}

/**
* @brief not-a-number (NaN) function
* @param arg Narrow character string identifying the contents of a NaN
* @return The quiet NaN value that corresponds to the identifying string arg or zero if the implementation does not support quiet NaNs.
*/
constexpr float nanf(const char * arg) noexcept
{
return ccm::internal::impl::nan_float_impl(arg);
}

constexpr long double nanl([[maybe_unused]] const char * arg) noexcept
/**
* @brief not-a-number (NaN) function
* @param arg Narrow character string identifying the contents of a NaN
* @return The quiet NaN value that corresponds to the identifying string arg or zero if the implementation does not support quiet NaNs.
* @note This function is not yet supported for long double and can only return either 0.0 or a quiet NaN based on if quiet NaNs are supported.
*/
constexpr long double nanl(const char * /* arg */) noexcept
{
if constexpr (!std::numeric_limits<long double>::is_iec559)
{
return 0.0;
}

// Currently we do not yet support long double for ccm::nan
// Idk if we ever will, but for the time being if someone calls the function. Just return a quiet NaN.
return std::numeric_limits<long double>::quiet_NaN();
Expand Down

0 comments on commit 58b5b70

Please sign in to comment.