16#include <fmt/format.h>
54 auto q =
value.native_handle();
58 if (requested == -1 && mpz_cmp_ui(mpq_denref(q), 1) == 0)
return value.to_string();
59 throw std::invalid_argument(
"exact decimal output requires an explicit digit count or fraction formatting");
62 throw std::invalid_argument(
"general decimal output requires a positive digit count");
64 mp_integer numerator, denominator, power, rounded,
remainder;
65 mpz_abs(numerator.value, mpq_numref(q));
66 mpz_set(denominator.value, mpq_denref(q));
67 bool const negative = mpq_sgn(q) < 0;
71 if (!
fixed && mpz_sgn(numerator.value) != 0)
74 exponent =
static_cast<long>(mpz_sizeinbase(numerator.value, 10)) -
75 static_cast<long>(mpz_sizeinbase(denominator.value, 10));
76 auto compare_power = [&](
long e) {
77 mpz_ui_pow_ui(power.value, 10,
static_cast<unsigned long>(e < 0 ? -e : e));
80 mpz_mul(
remainder.value, denominator.value, power.value);
81 return mpz_cmp(numerator.value,
remainder.value);
83 mpz_mul(
remainder.value, numerator.value, power.value);
84 return mpz_cmp(
remainder.value, denominator.value);
86 while (compare_power(exponent) < 0)
88 while (compare_power(exponent + 1) >= 0)
91 long const fractional =
general ?
static_cast<long>(requested) - 1 : requested;
92 long const scale =
fixed ? requested : fractional - exponent;
93 mpz_ui_pow_ui(power.value, 10,
static_cast<unsigned long>(
scale < 0 ? -
scale :
scale));
95 mpz_mul(numerator.value, numerator.value, power.value);
97 mpz_mul(denominator.value, denominator.value, power.value);
98 mpz_tdiv_qr(rounded.value,
remainder.value, numerator.value, denominator.value);
100 int const halfway = mpz_cmp(
remainder.value, denominator.value);
101 if (halfway > 0 || (halfway == 0 && mpz_odd_p(rounded.value))) mpz_add_ui(rounded.value, rounded.value, 1);
102 std::string digits(mpz_sizeinbase(rounded.value, 10) + 2,
'\0');
103 mpz_get_str(digits.data(), 10, rounded.value);
104 digits.resize(std::char_traits<char>::length(digits.c_str()));
105 if (!
fixed && digits.size() >
static_cast<std::size_t
>(fractional + 1))
111 long const decimal_places =
fixed ? requested : (
scientific ? fractional : fractional - exponent);
112 if (decimal_places > 0)
114 auto places =
static_cast<std::size_t
>(decimal_places);
115 if (digits.size() <= places) digits.insert(0, places + 1 - digits.size(),
'0');
116 digits.insert(digits.size() - places, 1,
'.');
119 while (digits.back() ==
'0')
121 if (digits.back() ==
'.') digits.pop_back();
124 if (
scientific) digits += fmt::format(
"e{}{:02}", exponent < 0 ?
'-' :
'+', exponent < 0 ? -exponent : exponent);
125 if (negative) digits.insert(0, 1,
'-');
129inline std::string format_mpreal(mpreal
const&
value, scalar_format_options
const& options)
131 if (
value.is_exact())
132 return options.exact_fractions ?
value.to_string() : format_exact_decimal(
value.exact_value(), options);
133 auto const requested = options.precision < 0 ? mpfr_get_str_ndigits(10,
value.precision().bit_count())
134 : static_cast<
std::size_t>(options.precision);
135 if (requested >
static_cast<std::size_t
>(std::numeric_limits<int>::max()))
136 throw std::length_error(
"mpreal: decimal formatting precision is too large");
140 char* buffer =
nullptr;
141 int const count = mpfr_asprintf(&buffer, format,
static_cast<int>(requested),
value.native_handle());
142 std::unique_ptr<char,
decltype(&mpfr_free_str)> owned(buffer, &mpfr_free_str);
143 if (count < 0)
throw std::runtime_error(
"mpreal: decimal formatting failed");
144 return std::string(buffer,
static_cast<std::size_t
>(count));
148template <
typename T> [[nodiscard]]
int effective_precision(scalar_format_options
const& options)
150 if (options.precision >= 0)
152 return options.precision;
175template <
typename T> [[nodiscard]]
bool scalar_signbit(T
value)
177 return std::signbit(
static_cast<long double>(
value));
180template <
typename T> [[nodiscard]] std::string fmt_real(T
value, scalar_format_options
const& options)
182 int const precision = effective_precision<T>(options);
183 switch (options.notation)
186 return fmt::format(
"{:.{}f}",
value, precision);
188 return fmt::format(
"{:.{}e}",
value, precision);
190 return fmt::format(
"{:.{}g}",
value, precision);
192 return fmt::format(
"{}",
value);
195#if UNI20_HAS_FLOAT128 && UNI20_FLOAT128_PROVIDER_MPLAPACK
196[[nodiscard]]
inline std::string format_float128(uni20::float128
value, scalar_format_options
const& options)
198#if defined(MPLAPACK_BINARY128_MODE) && (MPLAPACK_BINARY128_MODE == MPLAPACK_BINARY128_MODE_FLOAT128)
199 int const precision = effective_precision<uni20::float128>(options);
200 char const notation = printf_notation(options.notation);
201 std::string
const format = fmt::format(
"%.{}{}", precision, notation);
203 char fixed_buffer[128] = {};
204 int const fixed_count = ::strfromf128(fixed_buffer,
sizeof(fixed_buffer), format.c_str(),
value);
207 throw std::runtime_error(
"failed to format uni20::float128 value");
209 if (
static_cast<std::size_t
>(fixed_count) <
sizeof(fixed_buffer))
211 return std::string(fixed_buffer,
static_cast<std::size_t
>(fixed_count));
214 std::string dynamic_buffer(
static_cast<std::size_t
>(fixed_count) + 1,
'\0');
215 int const dynamic_count = ::strfromf128(dynamic_buffer.data(), dynamic_buffer.size(), format.c_str(),
value);
216 if (dynamic_count < 0)
218 throw std::runtime_error(
"failed to format uni20::float128 value");
220 dynamic_buffer.resize(
static_cast<std::size_t
>(dynamic_count));
221 return dynamic_buffer;
226 return fmt::format(
"{}",
static_cast<long double>(
value));
230[[nodiscard]]
inline uni20::float128 parse_float128(std::string_view
text)
232 std::string buffer(
text);
235#if defined(MPLAPACK_BINARY128_MODE) && (MPLAPACK_BINARY128_MODE == MPLAPACK_BINARY128_MODE_FLOAT128)
236 uni20::float128
const parsed = ::strtof128(buffer.c_str(), &end);
238 long double const parsed = std::strtold(buffer.c_str(), &end);
240 if (end == buffer.c_str() || *end !=
'\0')
242 throw std::invalid_argument(fmt::format(
"invalid float128 value: {}",
text));
246 throw std::out_of_range(fmt::format(
"float128 value out of range: {}",
text));
248 return static_cast<uni20::float128
>(parsed);
252template <
typename T> [[nodiscard]] T parse_builtin_real(std::string_view
text)
254 std::string buffer(
text);
258 if constexpr (std::same_as<T, float>)
260 float const parsed = std::strtof(buffer.c_str(), &end);
261 if (end == buffer.c_str() || *end !=
'\0')
263 throw std::invalid_argument(fmt::format(
"invalid real value: {}",
text));
267 throw std::out_of_range(fmt::format(
"real value out of range: {}",
text));
271 else if constexpr (std::same_as<T, double>)
273 double const parsed = std::strtod(buffer.c_str(), &end);
274 if (end == buffer.c_str() || *end !=
'\0')
276 throw std::invalid_argument(fmt::format(
"invalid real value: {}",
text));
280 throw std::out_of_range(fmt::format(
"real value out of range: {}",
text));
286 long double const parsed = std::strtold(buffer.c_str(), &end);
287 if (end == buffer.c_str() || *end !=
'\0')
289 throw std::invalid_argument(fmt::format(
"invalid real value: {}",
text));
293 throw std::out_of_range(fmt::format(
"real value out of range: {}",
text));
295 return static_cast<T
>(parsed);
308 using real_type = std::remove_cv_t<T>;
310 if constexpr (std::same_as<real_type, mpreal>)
312 if (options.normalize_negative_zero &&
value == 0)
value = mpreal(
value.precision());
313 return detail::format_mpreal(
value, options);
318 if (options.normalize_negative_zero &&
value == real_type{})
323#if UNI20_HAS_FLOAT128
324 if constexpr (std::same_as<real_type, uni20::float128>)
326 return detail::format_float128(
value, options);
331 return detail::fmt_real(
value, options);
344 using real_type = make_real_t<T>;
345 real_type imag =
value.imag();
346 bool const negative_imag = detail::scalar_signbit(imag) && !(options.normalize_negative_zero && imag == 0);
351 else if (options.normalize_negative_zero && imag == 0 && detail::scalar_signbit(imag))
357 std::string(options.imaginary_unit);
366template <RealOrComplex T>
369 if constexpr (Real<T>)
388 requires(!std::same_as<std::remove_cv_t<T>, mpreal>)
392 using real_type = std::remove_cv_t<T>;
393#if UNI20_HAS_FLOAT128
394 if constexpr (std::same_as<real_type, uni20::float128>)
396 return detail::parse_float128(
text);
401 return detail::parse_builtin_real<real_type>(
text);
408 requires std::same_as<std::remove_cv_t<T>, mpreal>
409[[nodiscard]] T
parse_real(std::string_view
text, Precision precision)
411 return mpreal(
text, precision);
437 if constexpr (std::same_as<std::remove_cv_t<T>,
mpreal>)
438 value = parse_real<T>(token,
value.precision());
441 value = parse_real<T>(token);
445 stream.setstate(std::ios::failbit);
452#if UNI20_HAS_FLOAT128
453template <>
struct fmt::formatter<
uni20::float128>
455 constexpr auto parse(format_parse_context& ctx)
457 auto it = ctx.begin();
458 if (it != ctx.end() && *it !=
'}')
460 throw fmt::format_error(
"uni20::float128 formatter currently supports only default '{}'");
465 template <
typename FormatContext>
auto format(uni20::float128
value, FormatContext& ctx)
const
471template <>
struct fmt::formatter<
uni20::
complex<uni20::float128>>
473 constexpr auto parse(format_parse_context& ctx)
475 auto it = ctx.begin();
476 if (it != ctx.end() && *it !=
'}')
478 throw fmt::format_error(
"uni20::complex<uni20::float128> formatter currently supports only default '{}'");
483 template <
typename FormatContext>
Owning arbitrary-size rational with exact, eager arithmetic.
Definition exact_rational.hpp:161
Owning exact rational or finite-precision MPFR real, with eager arithmetic.
Definition mpreal.hpp:74
locale_t value
Definition data_table.cpp:49
presentation::styled_text text
Definition display.cpp:141
std::istream & read_real(std::istream &stream, T &value)
Read a Uni20 real scalar from a stream token.
Definition scalar_io.hpp:425
std::string format_complex(T const &value, scalar_format_options const &options={})
Format a Uni20 complex scalar as real+imagi.
Definition scalar_io.hpp:342
T parse_real(std::string_view text)
Parse a Uni20 real scalar from text.
Definition scalar_io.hpp:390
typename detail::complex_type< Real >::type complex
Project-level complex scalar spelling.
Definition types.hpp:66
std::string format_real(T value, scalar_format_options const &options={})
Format a Uni20 real scalar.
Definition scalar_io.hpp:306
real_format_notation
Selects how real scalar values are rendered as text.
Definition scalar_io.hpp:29
std::string format_scalar(T const &value, scalar_format_options const &options={})
Format any Uni20 real or complex scalar.
Definition scalar_io.hpp:367
parse_result parse(CLI::App &app, int argc, char const *const *argv, parse_policy policy)
Parse argc/argv, preserving CLI11's argument boundaries and help precedence.
Definition cli.cpp:116
constexpr detail::scalar_math::imag_fn imag
Read the imaginary component by value; real zero retains the input's type and precision.
Definition math.hpp:673
Definition async/async.hpp:26
bool signbit(mpreal const &x)
Definition mpreal.hpp:642
void scale(Tensor &tensor, Scalar factor)
Multiply every stored numerical block by a scalar in place.
Definition block_tensor_linear.hpp:400
mpreal remainder(mpreal const &a, mpreal const &b)
Nearest-even-quotient remainder; exact operands retain exact rational results.
Definition mpreal_functions.hpp:534
Project-level numeric limits customization point.
Definition numeric_limits.hpp:16