@@ -53,6 +53,47 @@ constexpr char DECIMAL_POINT = '.';
53
53
// This is used to represent which direction the number should be rounded.
54
54
enum class RoundDirection { Up, Down, Even };
55
55
56
+ LIBC_INLINE RoundDirection get_round_direction (int last_digit, bool truncated,
57
+ bool is_negative) {
58
+ switch (fputil::quick_get_round ()) {
59
+ case FE_TONEAREST:
60
+ // Round to nearest, if it's exactly halfway then round to even.
61
+ if (last_digit != 5 ) {
62
+ return last_digit > 5 ? RoundDirection::Up : RoundDirection::Down;
63
+ } else {
64
+ return !truncated ? RoundDirection::Even : RoundDirection::Up;
65
+ }
66
+ case FE_DOWNWARD:
67
+ if (is_negative && (truncated || last_digit > 0 )) {
68
+ return RoundDirection::Up;
69
+ } else {
70
+ return RoundDirection::Down;
71
+ }
72
+ case FE_UPWARD:
73
+ if (!is_negative && (truncated || last_digit > 0 )) {
74
+ return RoundDirection::Up;
75
+ } else {
76
+ return RoundDirection::Down;
77
+ }
78
+ return is_negative ? RoundDirection::Down : RoundDirection::Up;
79
+ case FE_TOWARDZERO:
80
+ return RoundDirection::Down;
81
+ default :
82
+ return RoundDirection::Down;
83
+ }
84
+ }
85
+
86
+ template <typename T>
87
+ LIBC_INLINE constexpr cpp::enable_if_t <cpp::is_integral_v<T>, bool >
88
+ zero_after_digits (int32_t base_2_exp, int32_t digits_after_point, T mantissa) {
89
+ const int32_t required_twos = -base_2_exp - digits_after_point - 1 ;
90
+ const bool has_trailing_zeros =
91
+ required_twos <= 0 ||
92
+ (required_twos < 60 &&
93
+ multiple_of_power_of_2 (mantissa, static_cast <uint32_t >(required_twos)));
94
+ return has_trailing_zeros;
95
+ }
96
+
56
97
class PaddingWriter {
57
98
bool left_justified = false ;
58
99
bool leading_zeroes = false ;
@@ -127,7 +168,9 @@ class FloatWriter {
127
168
Writer *writer; // Writes to the final output.
128
169
PaddingWriter padding_writer; // Handles prefixes/padding, uses total_digits.
129
170
130
- int flush_buffer () {
171
+ int flush_buffer (bool round_up_max_blocks = false ) {
172
+ const char MAX_BLOCK_DIGIT = (round_up_max_blocks ? ' 0' : ' 9' );
173
+
131
174
// Write the most recent buffered block, and mark has_written
132
175
if (!has_written) {
133
176
has_written = true ;
@@ -174,12 +217,12 @@ class FloatWriter {
174
217
has_decimal_point) {
175
218
size_t digits_to_write = digits_before_decimal - total_digits_written;
176
219
if (digits_to_write > 0 ) {
177
- RET_IF_RESULT_NEGATIVE (writer->write (' 9 ' , digits_to_write));
220
+ RET_IF_RESULT_NEGATIVE (writer->write (MAX_BLOCK_DIGIT , digits_to_write));
178
221
}
179
222
RET_IF_RESULT_NEGATIVE (writer->write (DECIMAL_POINT));
180
223
if ((BLOCK_SIZE * max_block_count) - digits_to_write > 0 ) {
181
224
RET_IF_RESULT_NEGATIVE (writer->write (
182
- ' 9 ' , (BLOCK_SIZE * max_block_count) - digits_to_write));
225
+ MAX_BLOCK_DIGIT , (BLOCK_SIZE * max_block_count) - digits_to_write));
183
226
}
184
227
// add 1 for the decimal point
185
228
total_digits_written += BLOCK_SIZE * max_block_count + 1 ;
@@ -189,7 +232,8 @@ class FloatWriter {
189
232
190
233
// Clear the buffer of max blocks
191
234
if (max_block_count > 0 ) {
192
- RET_IF_RESULT_NEGATIVE (writer->write (' 9' , max_block_count * BLOCK_SIZE));
235
+ RET_IF_RESULT_NEGATIVE (
236
+ writer->write (MAX_BLOCK_DIGIT, max_block_count * BLOCK_SIZE));
193
237
total_digits_written += max_block_count * BLOCK_SIZE;
194
238
max_block_count = 0 ;
195
239
}
@@ -268,19 +312,23 @@ class FloatWriter {
268
312
end_buff[count] = int_to_str[count + 1 + (BLOCK_SIZE - block_digits)];
269
313
}
270
314
271
- char low_digit;
315
+ char low_digit = ' 0 ' ;
272
316
if (block_digits > 0 ) {
273
317
low_digit = end_buff[block_digits - 1 ];
274
318
} else if (max_block_count > 0 ) {
275
319
low_digit = ' 9' ;
276
- } else {
320
+ } else if (buffered_digits > 0 ) {
277
321
low_digit = block_buffer[buffered_digits - 1 ];
278
322
}
279
323
324
+ bool round_up_max_blocks = false ;
325
+
280
326
// Round up
281
327
if (round == RoundDirection::Up ||
282
328
(round == RoundDirection::Even && low_digit % 2 != 0 )) {
283
329
bool has_carry = true ;
330
+ round_up_max_blocks = true ; // if we're rounding up, we might need to
331
+ // round up the max blocks that are buffered.
284
332
// handle the low block that we're adding
285
333
for (int count = static_cast <int >(block_digits) - 1 ;
286
334
count >= 0 && has_carry; --count) {
@@ -289,6 +337,8 @@ class FloatWriter {
289
337
} else {
290
338
end_buff[count] += 1 ;
291
339
has_carry = false ;
340
+ round_up_max_blocks = false ; // If the low block isn't all nines, then
341
+ // the max blocks aren't rounded up.
292
342
}
293
343
}
294
344
// handle the high block that's buffered
@@ -333,7 +383,7 @@ class FloatWriter {
333
383
// Either we intend to round down, or the rounding up is complete. Flush the
334
384
// buffers.
335
385
336
- RET_IF_RESULT_NEGATIVE (flush_buffer ());
386
+ RET_IF_RESULT_NEGATIVE (flush_buffer (round_up_max_blocks ));
337
387
338
388
// And then write the final block.
339
389
RET_IF_RESULT_NEGATIVE (writer->write ({end_buff, block_digits}));
@@ -571,42 +621,11 @@ LIBC_INLINE int convert_float_decimal_typed(Writer *writer,
571
621
digits /= 10 ;
572
622
}
573
623
RoundDirection round;
574
- // Is m * 10^(additionalDigits + 1) / 2^(-exponent) integer?
575
- const int32_t requiredTwos =
576
- -(exponent - MANT_WIDTH) - static_cast <int32_t >(precision) - 1 ;
577
- const bool trailingZeros =
578
- requiredTwos <= 0 ||
579
- (requiredTwos < 60 &&
580
- multiple_of_power_of_2 (float_bits.get_explicit_mantissa (),
581
- static_cast <uint32_t >(requiredTwos)));
582
- switch (fputil::quick_get_round ()) {
583
- case FE_TONEAREST:
584
- // Round to nearest, if it's exactly halfway then round to even.
585
- if (last_digit != 5 ) {
586
- round = last_digit > 5 ? RoundDirection::Up : RoundDirection::Down;
587
- } else {
588
- round = trailingZeros ? RoundDirection::Even : RoundDirection::Up;
589
- }
590
- break ;
591
- case FE_DOWNWARD:
592
- if (is_negative && (!trailingZeros || last_digit > 0 )) {
593
- round = RoundDirection::Up;
594
- } else {
595
- round = RoundDirection::Down;
596
- }
597
- break ;
598
- case FE_UPWARD:
599
- if (!is_negative && (!trailingZeros || last_digit > 0 )) {
600
- round = RoundDirection::Up;
601
- } else {
602
- round = RoundDirection::Down;
603
- }
604
- round = is_negative ? RoundDirection::Down : RoundDirection::Up;
605
- break ;
606
- case FE_TOWARDZERO:
607
- round = RoundDirection::Down;
608
- break ;
609
- }
624
+ const bool truncated =
625
+ !zero_after_digits (exponent - MANT_WIDTH, precision,
626
+ float_bits.get_explicit_mantissa ());
627
+ round = get_round_direction (last_digit, truncated, is_negative);
628
+
610
629
RET_IF_RESULT_NEGATIVE (
611
630
float_writer.write_last_block_dec (digits, maximum, round));
612
631
break ;
0 commit comments