Skip to content

Commit 81bc8d0

Browse files
Fix printing decimal-point character on printf %#a (#1296)
The C standard indicates that when processing the a conversion specifier "if the precision is zero *and* the # flag is not specified, no decimal- point character appears.". This means that __fmt needs to ensure that it prints the decimal-point character not only when the precision is non-0, but also when the # flag is specified - cosmopolitan currently does not. This patch fixes this, along with adding a few tests for this behaviour.
1 parent ef62730 commit 81bc8d0

File tree

2 files changed

+26
-5
lines changed

2 files changed

+26
-5
lines changed

libc/stdio/fmt.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1497,9 +1497,13 @@ int __fmt(void *fn, void *arg, const char *format, va_list va, int *wrote) {
14971497
i1 = prec1 & 7;
14981498
k = prec1 >> 3;
14991499
__FMT_PUT(alphabet[(fpb.bits[k] >> 4 * i1) & 0xf]);
1500-
if (prec1 > 0 || prec > 0) {
1500+
1501+
// decimal-point character appears if the precision isn't 0
1502+
// or the # flag is specified
1503+
if (prec1 > 0 || prec > 0 || (flags & FLAGS_HASH)) {
15011504
__FMT_PUT('.');
15021505
}
1506+
15031507
while (prec1 > 0) {
15041508
if (--i1 < 0) {
15051509
if (--k < 0)

test/libc/stdio/snprintf_test.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -217,12 +217,24 @@ TEST(snprintf, testLongDoubleRounding) {
217217
ASSERT_EQ(0, fesetround(previous_rounding));
218218
}
219219

220-
void check_a_conversion_specifier_prec_1(const char *result_str, double value) {
220+
void check_a_conversion_specifier_double(const char *fmt, const char *expected_str, double value) {
221221
char buf[30] = {0};
222-
int i = snprintf(buf, sizeof(buf), "%.1a", value);
222+
int i = snprintf(buf, sizeof(buf), fmt, value);
223223

224-
ASSERT_EQ(strlen(result_str), i);
225-
ASSERT_STREQ(result_str, buf);
224+
ASSERT_EQ(strlen(expected_str), i);
225+
ASSERT_STREQ(expected_str, buf);
226+
}
227+
228+
void check_a_conversion_specifier_long_double(const char *fmt, const char *expected_str, long double value) {
229+
char buf[30] = {0};
230+
int i = snprintf(buf, sizeof(buf), fmt, value);
231+
232+
ASSERT_EQ(strlen(expected_str), i);
233+
ASSERT_STREQ(expected_str, buf);
234+
}
235+
236+
void check_a_conversion_specifier_prec_1(const char *expected_str, double value) {
237+
check_a_conversion_specifier_double("%.1a", expected_str, value);
226238
}
227239

228240
TEST(snprintf, testAConversionSpecifierRounding) {
@@ -247,6 +259,11 @@ TEST(snprintf, testAConversionSpecifier) {
247259
check_a_conversion_specifier_prec_1("0x1.6p+4", 0x1.68p+4);
248260
check_a_conversion_specifier_prec_1("0x1.ap+4", 0x1.98p+4);
249261
check_a_conversion_specifier_prec_1("0x1.ap+4", 0x1.a8p+4);
262+
263+
check_a_conversion_specifier_double("%#a", "0x0.p+0", 0x0.0p0);
264+
check_a_conversion_specifier_double("%#A", "0X0.P+0", 0x0.0p0);
265+
check_a_conversion_specifier_long_double("%#La", "0x0.p+0", 0x0.0p0L);
266+
check_a_conversion_specifier_long_double("%#LA", "0X0.P+0", 0x0.0p0L);
250267
}
251268

252269
TEST(snprintf, apostropheFlag) {

0 commit comments

Comments
 (0)