17 template<WriterImpl WI>
22 template<
typename From,
typename To = u64>
24 static_assert(
sizeof(From) ==
sizeof(To));
25 return std::bit_cast<To>(f);
28 struct DoubleExtraction {
30 static_assert(std::numeric_limits<f64>::is_iec559 && std::numeric_limits<f64>::digits == 53 &&
31 std::numeric_limits<f64>::max_exponent == 1024);
33 static constexpr auto sigSize = std::numeric_limits<f64>::digits;
34 static constexpr auto expBias = std::numeric_limits<f64>::max_exponent - 1 + (sigSize - 1);
35 static constexpr auto maxIeeeExp =
static_cast<u64>(2 * std::numeric_limits<f64>::max_exponent - 1);
36 static constexpr auto hiddenBit =
static_cast<u64>(1) << (sigSize - 1);
37 static constexpr auto sigMask = hiddenBit - 1;
38 static constexpr auto expMask = maxIeeeExp << (sigSize - 1);
39 static constexpr auto signMask = ~((~static_cast<u64>(0)) >> 1);
43 static auto from_bits(
u64 bits) -> DoubleExtraction;
45 static auto from_float(
f64 f) -> DoubleExtraction;
47 [[nodiscard]]
auto phys_significand()
const ->
u64;
49 [[nodiscard]]
auto phys_exp()
const ->
u64;
51 [[nodiscard]]
auto is_finite()
const -> bool;
53 [[nodiscard]]
auto is_inf()
const -> bool;
55 [[nodiscard]]
auto is_nan()
const -> bool;
57 [[nodiscard]]
auto is_zero()
const -> bool;
59 [[nodiscard]]
auto sign_bit()
const -> bool;
62 auto int_floor_div_pow_2(
i32 x,
i32 p) ->
i32;
64 auto int_floor_log2_pow10(
i32 x) ->
i32;
66 auto int_floor_log10_pow2(
i32 x) ->
i32;
68 auto int_floor_log7p5_pow2(
i32 x) ->
i32;
74#ifdef __SIZEOF_INT128__
75 [[nodiscard]]
static U128 mul(
u64 l,
u64 remainder) {
76 __extension__
using u128 =
unsigned __int128;
77 const auto f = u128{l} * u128{remainder};
78 const auto high =
static_cast<u64>((f >> 64) & 0xFFFFFFFFFFFFFFFF);
79 const auto low =
static_cast<u64>((f >> 0) & 0xFFFFFFFFFFFFFFFF);
82#elif defined(_MSC_VER) && defined(_M_X64)
83 [[nodiscard]]
static U128 mul(
u64 l,
u64 remainder) {
85 u64 low = _umul128(l, remainder, &high);
89 [[nodiscard]]
static auto low32(
u64 x) ->
u32 {
return static_cast<u32>((x >> 0) & 0xFFFFFFFF); }
90 [[nodiscard]]
static auto high32(
u64 x) ->
u32 {
return static_cast<u32>((x >> 32) & 0xFFFFFFFF); }
92 [[nodiscard]]
static U128 mul(
u64 l,
u64 remainder) {
93 const auto mult_ll =
static_cast<u64>(low32(l)) *
static_cast<u64>(low32(remainder));
94 const auto mult_lh =
static_cast<u64>(low32(l)) *
static_cast<u64>(high32(remainder));
95 const auto mult_hl =
static_cast<u64>(high32(l)) *
static_cast<u64>(low32(remainder));
96 const auto mult_hh =
static_cast<u64>(high32(l)) *
static_cast<u64>(high32(remainder));
98 const auto mid1 = mult_hl + high32(mult_ll);
99 const auto mid2 = mult_lh + low32(mid1);
101 const auto high = mult_hh + high32(mid1) + high32(mid2);
102 const auto low = low32(mult_ll) | (
static_cast<u64>(low32(mid2)) << 32);
107 [[nodiscard]]
auto mul_shift(
u64 x)
const ->
u64;
109 [[nodiscard]]
auto mul_parity(
u64 twoF,
u32 betaM1)
const -> bool;
158 template<WriterImpl WI>
164 fmtStr = fmtStr.
sub(1);
168 fmtStr = fmtStr.
sub(2);
172 opts.capitalE =
true;
173 fmtStr = fmtStr.
sub(1);
176 opts.capitalE =
false;
177 fmtStr = fmtStr.
sub(1);
181 opts.capitalE =
true;
182 opts.forceScientific =
true;
183 fmtStr = fmtStr.
sub(1);
186 opts.forceScientific =
true;
187 fmtStr = fmtStr.
sub(1);
192 fmtStr = fmtStr.
sub(1);
197 const auto decSpecifier = fmtStr.
sub(pivot + 1);
198 ensure(!decSpecifier.empty(),
"Invalid format string");
200 const auto sepIndex =
slices::first_index(
'-', decSpecifier).value_or(decSpecifier.size());
201 const auto minDigits = decSpecifier.sub(0, sepIndex);
202 ensure(!minDigits.empty(),
"Invalid format string");
205 ensure(opts.minDecPlaces >= 0);
207 opts.maxDecPlaces = opts.minDecPlaces;
208 if (
const auto maxDigits = decSpecifier.sub(sepIndex + 1); !maxDigits.empty()) {
210 ensure(opts.maxDecPlaces >= opts.minDecPlaces);
211 ensure(opts.maxDecPlaces <= 16);
214 fmtStr = fmtStr.
sub(0, pivot);
216 if (opts.maxDecPlaces <= 16) {
226 fmtStr = fmtStr.
sub(fmtStr.
size());
231 ensure(opts.precision <= 16);
232 auto b = std::array<char, 100>{};
234 auto printRes =
to_chars(buff, f, opts);
235 if (printRes.is_error()) {
237 return error(Err::OUT_OF_ROOM);
240 if (opts.minDecPlaces == 0 &&
static_cast<size_t>(opts.maxDecPlaces) >= printRes.value().size()) {
241 return writer.write_all(printRes.value()).with_success_val(printRes.value().size());
244 auto raw = printRes.value().to_const();
246 return writer.write_all(printRes.value()).with_success_val(printRes.value().size());
250 if (decPointIndex.empty()) {
251 if (opts.minDecPlaces == 0) {
252 return writer.write_all(printRes.value()).with_success_val(printRes.value().size());
260 auto end = raw.size();
262 if (sciNoteLowerE.has_value()) {
263 end = sciNoteLowerE.value();
265 else if (sciNoteUpperE.has_value()) {
266 end = sciNoteUpperE.value();
269 auto toPrint = raw.sub(0, end);
271 if (
auto r = writer.write_all(toPrint); r.is_error()) {
274 written += toPrint.size();
276 if (
auto r = writer.write(
'.'); r.is_error()) {
281 if (
auto r = writer.write_n_times(
'0',
static_cast<size_t>(opts.minDecPlaces)); r.is_error()) {
284 written += opts.minDecPlaces;
285 auto rest = raw.sub(end);
287 if (
auto r = writer.write_all(rest); r.is_error()) {
290 written += rest.size();
296 auto end = raw.size();
300 if (sciNoteLowerE.has_value()) {
301 end = sciNoteLowerE.value();
303 else if (sciNoteUpperE.has_value()) {
304 end = sciNoteUpperE.value();
307 auto decs = raw.sub(0, end).sub(decPointIndex.value() + 1);
308 auto numDigits = decs.size();
309 if (
static_cast<i32>(numDigits) >= opts.minDecPlaces &&
static_cast<i32>(numDigits) <= opts.maxDecPlaces) {
310 return writer.write_all(printRes.value()).with_success_val(printRes.value().size());
313 auto start = raw.sub(0, decPointIndex.value() + 1);
316 if (
auto r = writer.write_all(start); r.is_error()) {
319 written += start.size();
321 if (
static_cast<i32>(numDigits) < opts.minDecPlaces) {
322 if (
auto r = writer.write_all(decs); r.is_error()) {
325 written += decs.size();
327 auto toWrite = opts.minDecPlaces - decs.size();
328 if (
auto r = writer.write_n_times(
'0', toWrite); r.is_error()) {
334 auto decsToWrite = decs.sub(0, opts.maxDecPlaces);
335 if (
auto r = writer.write_all(decsToWrite); r.is_error()) {
338 written += decsToWrite.size();
340 auto finish = raw.sub(end);
341 if (!finish.empty()) {
342 if (
auto r = writer.write_all(finish); r.is_error()) {
345 written += finish.size();
Result< Out, AsciiNumericParseError > base10_to_int(const Slice< const char > &parse)
Tries to parse an ASCII numeric string into an integer Will return an error if the parsed string does...
bool is_pos_int_str(Slice< const char > str)
Checks if a string is composed of only positive integer characters Will return false if string is emp...
constexpr Slice< char32_t > mut_slice_from(char32_t *cstr, size_t len)
Creates a mutable slice from a utf32 string and length.
bool starts_with(const Slice< T > &needle, const Slice< T > &haystack)
Checks whether a slice (the haystack) starts with elements in another slice in the same order (the ne...
constexpr Slice< const char32_t > slice_from(char32_t *cstr)
Creates a slice from a utf32 string in the form of a c string.
mtcore::Optional< size_t > first_index(const Slice< T > &needle, const Slice< T > &haystack)
Gets the first index that a needle appears in the haystack, or nullopt if the needle does not appear ...
bool contains(const std::remove_const_t< T > &needle, const Slice< T > &haystack)
Checks whether a slice (the haystack) contains an element (the needle) Uses the equality operator of ...
SliceWriteError
Errors when writing to a slice.
#define ensure(check,...)
Ensures that a check holds true, aborts the program if not true Will print error if the condition is ...
Success< void > success()
Creates a successful void Result object.
Error< Underlying > error(Underlying err)
Creates an error.
uint64_t u64
Alias for 64-bit unsigned ints.
int32_t i32
Alias for 32-bit ints.
double f64
Alias for 64-bit floats.
int8_t i8
Alias for 8-bit ints.
uint32_t u32
Alias for 32-bit unsigned ints.
Implementation of the dragonbox algorithm to convert floats to strings Uses slices and writers rather...
auto print_dec_digs_reversed(Slice< char > buff, u64 out) -> i32
auto trailing_zeros_2digs(u32 digits) -> i8
void unsigned_to_ascii_2digs(Slice< char > buff, u32 digits)
auto format_digits(Slice< char > buff, u64 digits, i32 decExp, DragonboxFormatOptions={}) -> Slice< char >
auto unsigned_to_ascii_8digits_skip_trailing_zeros(Slice< char > buff, u32 digits) -> i32
auto compute_pow10(i32 p) -> impl::U128
auto multiple_of_pow5(u64 val, i32 exp) -> bool
auto to_chars(Slice< char > buff, double val, DragonboxFormatOptions={}) -> Result< Slice< char >, SliceWriteError >
auto round_compute_pow10_double(size_t d) -> double
auto multiple_of_pow2(u64 val, i32 exp)
Result< size_t, typename Writer< WI >::ErrType > format_float(Writer< WI > &writer, f64 f, Slice< const char > opts={nullptr, 0})
Represents a Result that may have an error (error code) or a success value A type of "void" means the...
A Slice which is just a pointer + length Accessing elements through the array operator will do bounds...
constexpr Slice sub(size_t start) const noexcept
Gets a sub Slice from start.
constexpr size_t size() const noexcept
Gets the size of a Slice.
constexpr bool empty() const noexcept
Checks if a Slice is empty.
A writer that writes data to some sort of stream or buffer Note: the data elements written should be ...
typename Impl::ErrType ErrType
static auto from_parts(u64 ieeeSig, u64 ieeeExp) -> FloatDec64
static auto from_asymmetric_interval(const i32 exp) -> FloatDec64
static auto compute_delta(impl::U128 p10, i32 betaM1) -> u32
static auto is_integral_midpoint(u64 twoF, i32 exp, i32 minusK) -> bool
static auto is_integral_endpoint(u64 twoF, i32 exp, i32 minusK) -> bool