Partially fix printf hex float numbers/%a rounding

Hexadecimal printing of floating-point numbers on cosmopolitan (that is,
using the a conversion specifier) currently does not correctly round the
result in any rounding mode except for the default one (i.e. FE_NEAREST)

This commit fixes this, and adds tests for the change (note that there's
still some rounding issues with the a conversion specifier in general in
relatively rare cases (that is, without non-default rounding modes), but
this author gave up on fixing them after they tried to read the floating
point formatting code, and wounded up almost more confused than before).
This commit is contained in:
Gabriel Ravier 2024-09-10 16:02:41 +02:00
parent cceddd21b2
commit 64521d33f1
2 changed files with 61 additions and 8 deletions

View file

@ -690,26 +690,45 @@ static int __fmt_fpiprec(struct FPBits *b) {
// prec1 = incoming precision (after ".")
static int __fmt_bround(struct FPBits *b, int prec, int prec1) {
uint32_t *bits, t;
int i, inc, j, k, m, n;
int i, j, k, m, n;
bool inc = false;
int current_rounding_mode;
m = prec1 - prec;
bits = b->bits;
inc = 0;
k = m - 1;
// The first two ifs here handle cases where rounding is simple, i.e. where we
// always know in which direction we must round because of the current
// rounding mode (note that if the correct value for inc is `false` then it
// doesn't need to be set as we have already done so above)
// The last one handles rounding to nearest
current_rounding_mode = fegetround();
if (current_rounding_mode == FE_TOWARDZERO ||
(current_rounding_mode == FE_UPWARD && b->sign) ||
(current_rounding_mode == FE_DOWNWARD && !b->sign))
goto have_inc;
if ((current_rounding_mode == FE_UPWARD && !b->sign) ||
(current_rounding_mode == FE_DOWNWARD && b->sign)) {
inc = true;
goto have_inc;
}
if ((t = bits[k >> 3] >> (j = (k & 7) * 4)) & 8) {
if (t & 7)
goto inc1;
goto inc_true;
if (j && bits[k >> 3] << (32 - j))
goto inc1;
goto inc_true;
while (k >= 8) {
k -= 8;
if (bits[k >> 3]) {
inc1:
inc = 1;
goto haveinc;
inc_true:
inc = true;
goto have_inc;
}
}
}
haveinc:
have_inc:
b->ex += m * 4;
i = m >> 3;
k = prec1 >> 3;

View file

@ -216,3 +216,37 @@ TEST(snprintf, testLongDoubleRounding) {
ASSERT_EQ(0, fesetround(previous_rounding));
}
TEST(snprintf, testAConversionSpecifierRounding) {
int previous_rounding = fegetround();
ASSERT_EQ(0, fesetround(FE_DOWNWARD));
char buf[20];
int i = snprintf(buf, sizeof(buf), "%.1a", 0x1.fffffp+4);
ASSERT_EQ(8, i);
ASSERT_STREQ("0x1.fp+4", buf);
ASSERT_EQ(0, fesetround(FE_UPWARD));
i = snprintf(buf, sizeof(buf), "%.1a", 0x1.f8p+4);
ASSERT_EQ(8, i);
ASSERT_STREQ("0x2.0p+4", buf);
ASSERT_EQ(0, fesetround(previous_rounding));
}
// This test currently fails because of rounding issues
// If that ever gets fixed, uncomment this
/*
TEST(snprintf, testAConversionSpecifier) {
char buf[20];
int i = snprintf(buf, sizeof(buf), "%.1a", 0x1.7800000000001p+4);
ASSERT_EQ(8, i);
ASSERT_STREQ("0x1.8p+4", buf);
memset(buf, 0, sizeof(buf));
i = snprintf(buf, sizeof(buf), "%.1a", 0x1.78p+4);
ASSERT_EQ(8, i);
ASSERT_STREQ("0x1.8p+4", buf);
}
*/