2
0
mirror of https://github.com/boostorg/spirit.git synced 2026-01-19 04:42:11 +00:00

Merge pull request #843 from sergiud/long-double-precision-format

Karma: fix broken `real_generator` output for `long double`
This commit is contained in:
Nana Sakisaka
2025-12-21 06:48:42 +09:00
committed by GitHub
3 changed files with 58 additions and 14 deletions

View File

@@ -257,20 +257,28 @@ namespace boost { namespace spirit { namespace karma
// generate(sink, right_align(precision, '0')[ulong], n);
// but it's spelled out to avoid inter-modular dependencies.
unsigned int digits=1; //should be number of digits n(truncating any fraction)
if(!boost::spirit::traits::test_zero(n)) {
static constexpr uint64_t limit = UINT64_MAX / 10;
const T num = floor(n);
for (uint64_t x = 10u, i = 1u;; x *= 10, i++) {
if (num < x) {
digits=i;break;
}
if (x > limit) {
digits= i + 1;break;
}
}
}
// should be number of digits n(truncating any fraction)
typename remove_const<T>::type digits = 1;
if (!boost::spirit::traits::test_zero(n)) {
BOOST_CONSTEXPR_OR_CONST uint64_t limit =
std::numeric_limits<uint64_t>::max() / 10;
// Cannot cast T to uint64_t since the former might be a mocked
// type that does not support type casting
const T num = floor(n);
if (num > limit) {
// uint64_t cannot represent the fractional part of an 80
// bit floating point type at full precision. Fallback to
// convential computation.
digits = ceil(log10(n + T(1.)));
} else {
for (uint64_t x = 10; num >= x; x *= 10) {
++digits;
}
}
}
bool r = true;
for (/**/; r && digits < precision_; digits = digits + 1)
r = char_inserter<>::call(sink, '0');

View File

@@ -145,6 +145,7 @@ run regression_iterator.cpp ;
run regression_optional_double.cpp ;
run regression_real_0.cpp ;
run regression_real_policy_sign.cpp ;
run regression_real_policy_precision.cpp ;
run regression_real_scientific.cpp ;
run regression_semantic_action_attribute.cpp ;
run regression_unicode_char.cpp : : : <pch>off ;

View File

@@ -0,0 +1,35 @@
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/spirit/home/karma/numeric/real_policies.hpp>
#include <limits>
#include "real.hpp"
template <class T>
struct max_precision_policy : boost::spirit::karma::real_policies<T> {
static BOOST_CONSTEXPR unsigned precision(T /*unused*/) BOOST_NOEXCEPT {
return std::numeric_limits<T>::max_digits10;
}
};
int main() {
using namespace boost::spirit;
boost::spirit::karma::real_generator<long double,
max_precision_policy<long double>>
real;
BOOST_TEST(test("1.004999999999999999996", real, 1.005l));
BOOST_TEST(test("1.049999999999999999956", real, 1.05l));
BOOST_TEST(test("1.549999999999999999968", real, 1.55l));
// Construct the fractional part as the limit of uint64_t plus one as
// 1844674407370955265 = (2^64-1)/10)+1
// to ensure the formatting does not break above this internal limit.
BOOST_TEST(test("1.001844674407370955251", real, 1.001844674407370955251l));
BOOST_TEST(test("1.01844674407370955262", real, 1.01844674407370955262l));
BOOST_TEST(test("1.184467440737095526528", real, 1.184467440737095526528l));
return boost::report_errors();
}