Skip to content

Commit

Permalink
Fixes #153: Now applying roll-over for values rounded up using Banker…
Browse files Browse the repository at this point in the history
…'s rounding in `get_components()`

* Also added a comment-instead-of-a-testcase for a command in which that roll-over is meaningful
  • Loading branch information
eyalroz committed Jul 25, 2024
1 parent 64e6dfe commit 943989b
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 13 deletions.
24 changes: 11 additions & 13 deletions src/printf/printf.c
Original file line number Diff line number Diff line change
Expand Up @@ -609,9 +609,11 @@ static const floating_point_t powers_of_10[PRINTF_MAX_PRECOMPUTED_POWER_OF_10 +
#define PRINTF_MAX_SUPPORTED_PRECISION (NUM_DECIMAL_DIGITS_IN_INT64_T - 1)


// Break up a floating-point number - which is known to be a finite non-negative number -
// into its base-10 parts: integral - before the decimal point, and fractional - after it.
// Taken the precision into account, but does not change it even internally.
// Break up a non-negative, finite, floating-point number into two integral
// parts of its decimal representation: The number up to the decimal point,
// and the number appearing after that point - whose number of digits
// corresponds to the precision value. Example: The components of 12.621
// are 12 and 621 for precision 3, or 12 and 62 for precision 2.
static struct floating_point_components get_components(floating_point_t number, printf_size_t precision)
{
struct floating_point_components number_;
Expand All @@ -624,18 +626,14 @@ static struct floating_point_components get_components(floating_point_t number,
floating_point_t remainder = scaled_remainder - (floating_point_t) number_.fractional;
const floating_point_t one_half = (floating_point_t) 0.5;

if (remainder > one_half) {
if ((remainder > one_half) ||
// Banker's rounding, i.e. round half to even: 1.5 -> 2, but 2.5 -> 2
((remainder == one_half) && (number_.fractional & 1U))) {
++number_.fractional;
// handle rollover, e.g. case 0.99 with precision 1 is 1.0
if ((floating_point_t) number_.fractional >= powers_of_10[precision]) {
number_.fractional = 0;
++number_.integral;
}
}
else if ((remainder == one_half) && (number_.fractional & 1U)) {
// Banker's rounding, i.e. round half to even:
// 1.5 -> 2, but 2.5 -> 2
++number_.fractional;
if ((floating_point_t) number_.fractional >= powers_of_10[precision]) {
number_.fractional = 0;
++number_.integral;
}

if (precision == 0U) {
Expand Down
9 changes: 9 additions & 0 deletions test/test_suite_main_testcases.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1111,8 +1111,17 @@ PRINTF_TEST_CASE(misc)
PRINTING_CHECK("0.000000e+00", ==, sprintf_, buffer, "%e", 0.0);
PRINTING_CHECK("-0.000000e+00", ==, sprintf_, buffer, "%e", -0.0);
#endif
// What should we get with sprintf_(buffer, "%.2f", 0.995) ?
// that number cannot be represented as a double, and the closest double
// is actually 0.9949999999999999955591... and so on. That should give
// us 0.99 as the output.
// still, we can't complain about that value being confused with real 0.995, and
// so we can't complain if we get the banker's rounding for 0.995 to 1.00 as the
// output
//PRINTING_CHECK("1.00", ==, sprintf_, buffer, "%.2f", 0.995);
}


PRINTF_TEST_CASE(extremal_signed_integer_values)
{
char buffer[base_buffer_size];
Expand Down

0 comments on commit 943989b

Please sign in to comment.