Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions include/boost/decimal/charconv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <boost/decimal/detail/countl.hpp>
#include <boost/decimal/detail/remove_trailing_zeros.hpp>
#include <boost/decimal/detail/promotion.hpp>
#include <boost/decimal/detail/write_payload.hpp>

#ifndef BOOST_DECIMAL_BUILD_MODULE
#include <cstdint>
Expand Down Expand Up @@ -81,13 +82,26 @@ constexpr auto from_chars_general_impl(const char* first, const char* last, Targ
{
if (r.ec == std::errc::not_supported)
{
if (significand)
using resultant_sig_type = typename TargetDecimalType::significand_type;

resultant_sig_type payload_value {};
if (significand < std::numeric_limits<resultant_sig_type>::max())
{
payload_value = static_cast<resultant_sig_type>(significand);
}

if (expval > 0)
{
value = std::numeric_limits<TargetDecimalType>::signaling_NaN();
value = write_payload<TargetDecimalType, true>(payload_value);
}
else
{
value = std::numeric_limits<TargetDecimalType>::quiet_NaN();
value = write_payload<TargetDecimalType, false>(payload_value);
}

if (sign)
{
value = -value;
}

r.ec = std::errc();
Expand Down
4 changes: 2 additions & 2 deletions include/boost/decimal/decimal_fast128_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE TargetDecimalType>
constexpr auto to_chars_cohort_preserving_scientific(char* first, char* last, const TargetDecimalType& value) noexcept -> to_chars_result;

template <typename TargetDecimalType, bool is_snan>
constexpr auto nan_impl(const char* arg) noexcept
constexpr auto write_payload(typename TargetDecimalType::significand_type payload_value)
BOOST_DECIMAL_REQUIRES(detail::is_fast_type_v, TargetDecimalType);

} // namespace detail
Expand Down Expand Up @@ -196,7 +196,7 @@ BOOST_DECIMAL_EXPORT class decimal_fast128_t final
friend constexpr auto detail::to_chars_cohort_preserving_scientific(char* first, char* last, const TargetDecimalType& value) noexcept -> to_chars_result;

template <typename TargetDecimalType, bool is_snan>
friend constexpr auto detail::nan_impl(const char* arg) noexcept
friend constexpr auto detail::write_payload(typename TargetDecimalType::significand_type payload_value)
BOOST_DECIMAL_REQUIRES(detail::is_fast_type_v, TargetDecimalType);

template <typename T>
Expand Down
4 changes: 2 additions & 2 deletions include/boost/decimal/decimal_fast32_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ template <bool checked, BOOST_DECIMAL_DECIMAL_FLOATING_TYPE T>
constexpr auto d32_fma_impl(T x, T y, T z) noexcept -> T;

template <typename TargetDecimalType, bool is_snan>
constexpr auto nan_impl(const char* arg) noexcept
constexpr auto write_payload(typename TargetDecimalType::significand_type payload_value)
BOOST_DECIMAL_REQUIRES(detail::is_fast_type_v, TargetDecimalType);

} // namespace detail
Expand Down Expand Up @@ -196,7 +196,7 @@ BOOST_DECIMAL_EXPORT class decimal_fast32_t final
friend constexpr auto detail::generic_div_impl(const T& lhs, const T& rhs) noexcept -> DecimalType;

template <typename TargetDecimalType, bool is_snan>
friend constexpr auto detail::nan_impl(const char* arg) noexcept
friend constexpr auto detail::write_payload(typename TargetDecimalType::significand_type payload_value)
BOOST_DECIMAL_REQUIRES(detail::is_fast_type_v, TargetDecimalType);

template <typename T>
Expand Down
4 changes: 2 additions & 2 deletions include/boost/decimal/decimal_fast64_t.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ template <bool checked, BOOST_DECIMAL_DECIMAL_FLOATING_TYPE T>
constexpr auto d64_fma_impl(T x, T y, T z) noexcept -> T;

template <typename TargetDecimalType, bool is_snan>
constexpr auto nan_impl(const char* arg) noexcept
constexpr auto write_payload(typename TargetDecimalType::significand_type payload_value)
BOOST_DECIMAL_REQUIRES(detail::is_fast_type_v, TargetDecimalType);

} // namespace detail
Expand Down Expand Up @@ -203,7 +203,7 @@ BOOST_DECIMAL_EXPORT class decimal_fast64_t final
friend constexpr auto detail::d64_fma_impl(T x, T y, T z) noexcept -> T;

template <typename TargetDecimalType, bool is_snan>
friend constexpr auto detail::nan_impl(const char* arg) noexcept
friend constexpr auto detail::write_payload(typename TargetDecimalType::significand_type payload_value)
BOOST_DECIMAL_REQUIRES(detail::is_fast_type_v, TargetDecimalType);

template <typename T>
Expand Down
25 changes: 6 additions & 19 deletions include/boost/decimal/detail/cmath/nan.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <boost/decimal/detail/concepts.hpp>
#include <boost/decimal/detail/utilities.hpp>
#include <boost/decimal/detail/promotion.hpp>
#include <boost/decimal/detail/write_payload.hpp>
#include <boost/decimal/cstdlib.hpp>

#ifndef BOOST_DECIMAL_BUILD_MODULE
Expand All @@ -33,23 +34,16 @@ constexpr auto nan_impl(const char* arg) noexcept
constexpr TargetDecimalType nan_type {is_snan ? std::numeric_limits<TargetDecimalType>::signaling_NaN() :
std::numeric_limits<TargetDecimalType>::quiet_NaN()};

constexpr std::uint32_t significand_field_bits {decimal_val_v<TargetDecimalType> < 64 ? 23U :
decimal_val_v<TargetDecimalType> < 128 ? 53U : 110U};

constexpr sig_type max_payload_value {(static_cast<sig_type>(1) << significand_field_bits) - 1U};

sig_type payload_value {};
const auto r {from_chars_integer_impl<sig_type, sig_type>(arg, arg + detail::strlen(arg), payload_value, 10)};

TargetDecimalType return_value {nan_type};
if (!r || payload_value > max_payload_value)
if (!r)
{
return return_value;
return nan_type;
}
else
{
return_value.significand_ |= payload_value;
return return_value;
return write_payload<TargetDecimalType, is_snan>(payload_value);
}
}

Expand All @@ -62,23 +56,16 @@ constexpr auto nan_impl(const char* arg) noexcept
constexpr TargetDecimalType nan_type {is_snan ? std::numeric_limits<TargetDecimalType>::signaling_NaN() :
std::numeric_limits<TargetDecimalType>::quiet_NaN()};

constexpr std::uint32_t significand_field_bits {sizeof(TargetDecimalType) == sizeof(std::uint32_t) ? 23U :
sizeof(TargetDecimalType) == sizeof(std::uint64_t) ? 53U : 110U};

constexpr sig_type max_payload_value {(static_cast<sig_type>(1) << significand_field_bits) - 1U};
constexpr TargetDecimalType zero {};
constexpr TargetDecimalType zero_bits {zero ^ zero};

sig_type payload_value {};
const auto r {from_chars_integer_impl<sig_type, sig_type>(arg, arg + detail::strlen(arg), payload_value, 10)};

if (!r || payload_value > max_payload_value)
if (!r)
{
return nan_type;
}
else
{
return (zero_bits | payload_value) | nan_type;
return write_payload<TargetDecimalType, is_snan>(payload_value);
}
}

Expand Down
88 changes: 69 additions & 19 deletions include/boost/decimal/detail/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,15 @@ constexpr auto parser(const char* first, const char* last, bool& sign, Unsigned_
sign = false;
}

constexpr std::size_t significand_buffer_size = std::numeric_limits<Unsigned_Integer>::digits10 ;
char significand_buffer[significand_buffer_size] {};

// Handle non-finite values
// Stl allows for string like "iNf" to return inf
//
// This is nested ifs rather than a big one-liner to ensure that once we hit an invalid character
// or an end of buffer we return the correct value of next
bool signaling {};
if (next != last && (*next == 'i' || *next == 'I'))
{
++next;
Expand All @@ -112,14 +116,21 @@ constexpr auto parser(const char* first, const char* last, bool& sign, Unsigned_
if (next != last && (*next == 'f' || *next == 'F'))
{
++next;
significand = 0;
exponent = 0;
return {next, std::errc::value_too_large};
}
}

return {first, std::errc::invalid_argument};
}
else if (next != last && (*next == 'n' || *next == 'N'))

if (next != last && (*next == 's' || *next == 'S'))
{
++next;
signaling = true;
}

if (next != last && (*next == 'n' || *next == 'N'))
{
++next;
if (next != last && (*next == 'a' || *next == 'A'))
Expand All @@ -128,56 +139,97 @@ constexpr auto parser(const char* first, const char* last, bool& sign, Unsigned_
if (next != last && (*next == 'n' || *next == 'N'))
{
++next;
if (next != last && (*next == '('))
if (next != last)
{
const auto current_pos {next};
++next;

bool any_valid_char {false};
bool has_opening_brace {false};
if (*next == '(')
{
++next;
has_opening_brace = true;
}

// Handle nan(SNAN)
if ((last - next) >= 4 && (*next == 's' || *next == 'S') && (*(next + 1) == 'n' || *(next + 1) == 'N')
&& (*(next + 2) == 'a' || *(next + 2) == 'A') && (*(next + 3) == 'n' || *(next + 3) == 'N'))
{
next += 3;
significand = 1;
next += 4;
signaling = true;
any_valid_char = true;
}
// Handle Nan(IND)
else if ((last - next) >= 3 && (*next == 'i' || *next == 'I') && (*(next + 1) == 'n' || *(next + 1) == 'N')
&& (*(next + 2) == 'd' || *(next + 2) == 'D'))
{
next += 2;
significand = 0;
next += 3;
sign = true;
any_valid_char = true;
}

// Arbitrary payload
bool valid_payload {false};
// Arbitrary numerical payload
bool has_numerical_payload {false};
auto significand_buffer_first {significand_buffer};
std::size_t significand_characters {};
while (next != last && (*next != ')'))
{
if (is_payload_char(*next))
if (significand_characters < significand_buffer_size && is_integer_char(*next))
{
++next;
valid_payload = true;
++significand_characters;
*significand_buffer_first++ = *next++;
any_valid_char = true;
has_numerical_payload = true;
}
else
{
valid_payload = false;
// End of valid payload even if there are more characters
// e.g. SNAN42JUNK stops at J
break;
}
}

if (valid_payload)
// Non-numerical payload still needs to be parsed
// e.g. nan(PAYLOAD)
if (!has_numerical_payload && has_opening_brace)
{
while (next != last && (*next != ')'))
{
if (is_payload_char(*next))
{
any_valid_char = true;
++next;
}
else
{
break;
}
}
}

if (next != last && any_valid_char)
{
// One past the end if we need to
++next;
}
else

if (significand_characters != 0)
{
from_chars_dispatch(significand_buffer, significand_buffer + significand_characters, significand, 10);
}

if (!any_valid_char)
{
// If we have nan(..BAD..) we should point to (
next = current_pos;
}

exponent = static_cast<Integer>(signaling);
return {next, std::errc::not_supported};
}
else
{
significand = 0;
exponent = static_cast<Integer>(signaling);
return {next, std::errc::not_supported};
}
}
Expand All @@ -204,8 +256,6 @@ constexpr auto parser(const char* first, const char* last, bool& sign, Unsigned_
}

// Next we get the significand
constexpr std::size_t significand_buffer_size = std::numeric_limits<Unsigned_Integer>::digits10 ;
char significand_buffer[significand_buffer_size] {};
std::size_t i = 0;
std::size_t dot_position = 0;
Integer extra_zeros = 0;
Expand Down
69 changes: 69 additions & 0 deletions include/boost/decimal/detail/write_payload.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#ifndef BOOST_DECIMAL_DETAIL_WRITE_PAYLOAD_HPP
#define BOOST_DECIMAL_DETAIL_WRITE_PAYLOAD_HPP

#include <boost/decimal/detail/config.hpp>
#include <boost/decimal/detail/concepts.hpp>
#include <boost/decimal/detail/promotion.hpp>

namespace boost {
namespace decimal {
namespace detail {

template <typename TargetDecimalType, bool is_snan>
constexpr auto write_payload(typename TargetDecimalType::significand_type payload_value)
BOOST_DECIMAL_REQUIRES(detail::is_fast_type_v, TargetDecimalType)
{
using sig_type = typename TargetDecimalType::significand_type;

constexpr TargetDecimalType nan_type {is_snan ? std::numeric_limits<TargetDecimalType>::signaling_NaN() :
std::numeric_limits<TargetDecimalType>::quiet_NaN()};

constexpr std::uint32_t significand_field_bits {decimal_val_v<TargetDecimalType> < 64 ? 23U :
decimal_val_v<TargetDecimalType> < 128 ? 53U : 110U};

constexpr sig_type max_payload_value {(static_cast<sig_type>(1) << significand_field_bits) - 1U};

TargetDecimalType return_value {nan_type};
if (payload_value < max_payload_value)
{
return_value.significand_ |= payload_value;
}

return return_value;
}

template <typename TargetDecimalType, bool is_snan>
constexpr auto write_payload(typename TargetDecimalType::significand_type payload_value)
BOOST_DECIMAL_REQUIRES(detail::is_ieee_type_v, TargetDecimalType)
{
using sig_type = typename TargetDecimalType::significand_type;

constexpr TargetDecimalType nan_type {is_snan ? std::numeric_limits<TargetDecimalType>::signaling_NaN() :
std::numeric_limits<TargetDecimalType>::quiet_NaN()};

constexpr std::uint32_t significand_field_bits {decimal_val_v<TargetDecimalType> < 64 ? 23U :
decimal_val_v<TargetDecimalType> < 128 ? 53U : 110U};

constexpr sig_type max_payload_value {(static_cast<sig_type>(1) << significand_field_bits) - 1U};

constexpr TargetDecimalType zero {};
constexpr TargetDecimalType zero_bits {zero ^ zero};

TargetDecimalType return_value {nan_type};
if (payload_value < max_payload_value)
{
return_value = (zero_bits | payload_value) | nan_type;
}

return return_value;
}

} // namespace detail
} // namespace decimal
} // namespace boost

#endif // BOOST_DECIMAL_DETAIL_WRITE_PAYLOAD_HPP
1 change: 1 addition & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ run test_format_fmtlib.cpp ;
run-fail test_fprintf.cpp ;
run test_frexp_ldexp.cpp ;
run test_from_chars.cpp /boost/charconv//boost_charconv ;
run test_from_chars_nan_payloads.cpp ;
run test_git_issue_266.cpp ;
run test_git_issue_271.cpp ;
run test_hash.cpp ;
Expand Down
Loading