Skip to content

Commit 333c3d1

Browse files
Add the uppercase B conversion specifier to printf (#1300)
The (uppercase) B conversion specifier is specified by the C standard to have the same behavior as the (lowercase) b conversion specifier, except that whenever the # flag is used, the (uppercase) B conversion specifier alters a nonzero result by prefixing it with "0B", instead of with "0b". This commit adds this conversion specifier alongside a few tests for it.
1 parent 518eaba commit 333c3d1

File tree

2 files changed

+95
-114
lines changed

2 files changed

+95
-114
lines changed

libc/stdio/fmt.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1090,9 +1090,10 @@ int __fmt(void *fn, void *arg, const char *format, va_list va, int *wrote) {
10901090
case 'x':
10911091
log2base = 4;
10921092
goto FormatNumber;
1093+
case 'B':
10931094
case 'b':
10941095
log2base = 1;
1095-
alphabet = "0123456789abcdefpb";
1096+
alphabet = (d == 'b' ? "0123456789abcdefpb" : "0123456789ABCDEFPB");
10961097
goto FormatNumber;
10971098
case 'o':
10981099
log2base = 3;

test/libc/stdio/snprintf_test.c

Lines changed: 93 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,70 @@
2121
#include "libc/str/str.h"
2222
#include "libc/testlib/testlib.h"
2323

24+
static void check_single_double(const char *fmt, const char *expected_str,
25+
double value) {
26+
char buf[30] = {0};
27+
int i = snprintf(buf, sizeof(buf), fmt, value);
28+
29+
ASSERT_GE(sizeof(buf), strlen(expected_str));
30+
ASSERT_EQ(strlen(expected_str), i);
31+
ASSERT_STREQ(expected_str, buf);
32+
while (i < sizeof(buf))
33+
ASSERT_EQ('\0', buf[i++]);
34+
}
35+
36+
static void check_single_long_double(const char *fmt, const char *expected_str,
37+
long double value) {
38+
char buf[30] = {0};
39+
int i = snprintf(buf, sizeof(buf), fmt, value);
40+
41+
ASSERT_GE(sizeof(buf), strlen(expected_str));
42+
ASSERT_EQ(strlen(expected_str), i);
43+
ASSERT_STREQ(expected_str, buf);
44+
while (i < sizeof(buf))
45+
ASSERT_EQ('\0', buf[i++]);
46+
}
47+
48+
void check_single_long_double_arr_allowed(
49+
const char *fmt, const char *allowed_strs[], long double value) {
50+
char buf[30] = {0};
51+
int res = snprintf(buf, sizeof(buf), fmt, value);
52+
53+
for (size_t i = 0; allowed_strs[i] != NULL; ++i)
54+
if (strlen(allowed_strs[i]) == res && strcmp(allowed_strs[i], buf) == 0)
55+
return;
56+
57+
printf("Failed to find matching str for %`'s, allowed strs:\n", buf);
58+
for (size_t i = 0; allowed_strs[i] != NULL; ++i)
59+
printf("- %`'s\n", allowed_strs[i]);
60+
fflush(stdout);
61+
ASSERT_EQ(false, true);
62+
}
63+
64+
static void check_single_int(const char *fmt, const char *expected_str,
65+
int value) {
66+
char buf[30] = {0};
67+
int i = snprintf(buf, sizeof(buf), fmt, value);
68+
69+
ASSERT_GE(sizeof(buf), strlen(expected_str));
70+
ASSERT_EQ(strlen(expected_str), i);
71+
ASSERT_STREQ(expected_str, buf);
72+
while (i < sizeof(buf))
73+
ASSERT_EQ('\0', buf[i++]);
74+
}
75+
76+
static void check_single_wint_t(const char *fmt, const char *expected_str,
77+
wint_t value) {
78+
char buf[30] = {0};
79+
int i = snprintf(buf, sizeof(buf), fmt, value);
80+
81+
ASSERT_GE(sizeof(buf), strlen(expected_str));
82+
ASSERT_EQ(strlen(expected_str), i);
83+
ASSERT_STREQ(expected_str, buf);
84+
while (i < sizeof(buf))
85+
ASSERT_EQ('\0', buf[i++]);
86+
}
87+
2488
TEST(snprintf, testVeryLargePrecision) {
2589
char buf[512] = {};
2690
int i = snprintf(buf, sizeof(buf), "%.9999u", 10);
@@ -30,59 +94,21 @@ TEST(snprintf, testVeryLargePrecision) {
3094
}
3195

3296
TEST(snprintf, testPlusFlagOnChar) {
33-
char buf[10] = {};
34-
int i = snprintf(buf, sizeof(buf), "%+c", '=');
35-
36-
ASSERT_EQ(1, i);
37-
ASSERT_STREQ("=", buf);
97+
check_single_int("%+c", "=", '=');
3898
}
3999

40100
TEST(snprintf, testInf) {
41-
char buf[10] = {};
42-
int i = snprintf(buf, sizeof(buf), "%f", 1.0 / 0.0);
43-
44-
ASSERT_EQ(3, i);
45-
ASSERT_STREQ("inf", buf);
46-
47-
memset(buf, '\0', 4);
48-
i = snprintf(buf, sizeof(buf), "%Lf", 1.0L / 0.0L);
49-
ASSERT_EQ(3, i);
50-
ASSERT_STREQ("inf", buf);
51-
52-
memset(buf, '\0', 4);
53-
i = snprintf(buf, sizeof(buf), "%e", 1.0 / 0.0);
54-
ASSERT_EQ(3, i);
55-
ASSERT_STREQ("inf", buf);
56-
57-
memset(buf, '\0', 4);
58-
i = snprintf(buf, sizeof(buf), "%Le", 1.0L / 0.0L);
59-
ASSERT_EQ(3, i);
60-
ASSERT_STREQ("inf", buf);
61-
62-
memset(buf, '\0', 4);
63-
i = snprintf(buf, sizeof(buf), "%g", 1.0 / 0.0);
64-
ASSERT_EQ(3, i);
65-
ASSERT_STREQ("inf", buf);
66-
67-
memset(buf, '\0', 4);
68-
i = snprintf(buf, sizeof(buf), "%Lg", 1.0L / 0.0L);
69-
ASSERT_EQ(3, i);
70-
ASSERT_STREQ("inf", buf);
71-
72-
for (i = 4; i < 10; ++i)
73-
ASSERT_EQ('\0', buf[i]);
101+
check_single_double("%f", "inf", 1.0 / 0.0);
102+
check_single_long_double("%Lf", "inf", 1.0L / 0.0L);
103+
check_single_double("%e", "inf", 1.0 / 0.0);
104+
check_single_long_double("%Le", "inf", 1.0L / 0.0L);
105+
check_single_double("%g", "inf", 1.0 / 0.0);
106+
check_single_long_double("%Lg", "inf", 1.0L / 0.0L);
74107
}
75108

76109
TEST(snprintf, testUppercaseCConversionSpecifier) {
77-
char buf[10] = {};
78-
int i = snprintf(buf, sizeof(buf), "%C", L'a');
79-
80-
ASSERT_EQ(1, i);
81-
ASSERT_STREQ("a", buf);
82-
83-
i = snprintf(buf, sizeof(buf), "%C", L'☺');
84-
ASSERT_EQ(3, i);
85-
ASSERT_STREQ("☺", buf);
110+
check_single_wint_t("%C", "a", L'a');
111+
check_single_wint_t("%C", "☺", L'☺');
86112
}
87113

88114
// Make sure we don't va_arg the wrong argument size on wide character
@@ -188,74 +214,26 @@ TEST(snprintf, testNConversionSpecifier) {
188214
}
189215

190216
TEST(snprintf, testLongDoubleEConversionSpecifier) {
191-
char buf[20] = {};
192-
int i = snprintf(buf, sizeof(buf), "%Le", 1234567.8L);
193-
194-
ASSERT_EQ(12, i);
195-
ASSERT_STREQ("1.234568e+06", buf);
217+
check_single_long_double("%Le", "1.234568e+06", 1234567.8L);
196218
}
197219

198220
TEST(snprintf, testLongDoubleRounding) {
199221
int previous_rounding = fegetround();
200222
ASSERT_EQ(0, fesetround(FE_DOWNWARD));
201223

202-
char buf[20];
203-
int i = snprintf(buf, sizeof(buf), "%.3Lf", 4.4375L);
204-
ASSERT_EQ(5, i);
205-
ASSERT_STREQ("4.437", buf);
206-
207-
i = snprintf(buf, sizeof(buf), "%.3Lf", -4.4375L);
208-
ASSERT_EQ(6, i);
209-
ASSERT_STREQ("-4.438", buf);
224+
check_single_long_double("%.3Lf", "4.437", 4.4375L);
225+
check_single_long_double("%.3Lf", "-4.438", -4.4375L);
210226

211227
ASSERT_EQ(0, fesetround(FE_TOWARDZERO));
212228

213-
i = snprintf(buf, sizeof(buf), "%.3Lf", -4.4375L);
214-
ASSERT_EQ(6, i);
215-
ASSERT_STREQ("-4.437", buf);
229+
check_single_long_double("%.3Lf", "-4.437", -4.4375L);
216230

217231
ASSERT_EQ(0, fesetround(previous_rounding));
218232
}
219233

220-
void check_a_conversion_specifier_double(const char *fmt,
221-
const char *expected_str,
222-
double value) {
223-
char buf[30] = {0};
224-
int i = snprintf(buf, sizeof(buf), fmt, value);
225-
226-
ASSERT_EQ(strlen(expected_str), i);
227-
ASSERT_STREQ(expected_str, buf);
228-
}
229-
230-
void check_a_conversion_specifier_long_double(const char *fmt,
231-
const char *expected_str,
232-
long double value) {
233-
char buf[30] = {0};
234-
int i = snprintf(buf, sizeof(buf), fmt, value);
235-
236-
ASSERT_EQ(strlen(expected_str), i);
237-
ASSERT_STREQ(expected_str, buf);
238-
}
239-
240-
void check_a_conversion_specifier_long_double_arr_allowed(
241-
const char *fmt, const char *allowed_strs[], long double value) {
242-
char buf[30] = {0};
243-
int res = snprintf(buf, sizeof(buf), fmt, value);
244-
245-
for (size_t i = 0; allowed_strs[i] != NULL; ++i)
246-
if (strlen(allowed_strs[i]) == res && strcmp(allowed_strs[i], buf) == 0)
247-
return;
248-
249-
printf("Failed to find matching str for %`'s, allowed strs:\n", buf);
250-
for (size_t i = 0; allowed_strs[i] != NULL; ++i)
251-
printf("- %`'s\n", allowed_strs[i]);
252-
fflush(stdout);
253-
ASSERT_EQ(false, true);
254-
}
255-
256234
void check_a_conversion_specifier_double_prec_1(const char *expected_str,
257235
double value) {
258-
check_a_conversion_specifier_double("%.1a", expected_str, value);
236+
check_single_double("%.1a", expected_str, value);
259237
}
260238

261239
TEST(snprintf, testAConversionSpecifierRounding) {
@@ -281,23 +259,25 @@ TEST(snprintf, testAConversionSpecifier) {
281259
check_a_conversion_specifier_double_prec_1("0x1.ap+4", 0x1.98p+4);
282260
check_a_conversion_specifier_double_prec_1("0x1.ap+4", 0x1.a8p+4);
283261

284-
check_a_conversion_specifier_double("%#a", "0x0.p+0", 0x0.0p0);
285-
check_a_conversion_specifier_double("%#A", "0X0.P+0", 0x0.0p0);
286-
check_a_conversion_specifier_long_double("%#La", "0x0.p+0", 0x0.0p0L);
287-
check_a_conversion_specifier_long_double("%#LA", "0X0.P+0", 0x0.0p0L);
288-
289-
check_a_conversion_specifier_double("%.2a", "0x1.00p-1026", 0xf.fffp-1030);
262+
check_single_double("%#a", "0x0.p+0", 0x0.0p0);
263+
check_single_double("%#A", "0X0.P+0", 0x0.0p0);
264+
check_single_long_double("%#La", "0x0.p+0", 0x0.0p0L);
265+
check_single_long_double("%#LA", "0X0.P+0", 0x0.0p0L);
290266

291-
check_a_conversion_specifier_double("%.1a", "0x2.0p+0", 1.999);
267+
check_single_double("%.2a", "0x1.00p-1026", 0xf.fffp-1030);
292268

269+
check_single_double("%.1a", "0x2.0p+0", 1.999);
293270
const char *acceptable_results1[] = {"0x1.0p+1", "0x2.0p+0", NULL};
294-
check_a_conversion_specifier_long_double_arr_allowed(
271+
check_single_long_double_arr_allowed(
295272
"%.1La", acceptable_results1, 1.999L);
296273
}
297274

298-
TEST(snprintf, apostropheFlag) {
299-
char buf[20];
300-
int i = snprintf(buf, sizeof(buf), "%'d", 1000000);
301-
ASSERT_EQ(7, i);
302-
ASSERT_STREQ("1000000", buf);
275+
TEST(snprintf, testApostropheFlag) {
276+
check_single_int("%'d", "10000000", 10000000);
277+
}
278+
279+
TEST(snprintf, testUppercaseBConversionSpecifier) {
280+
check_single_int("%B", "0", 0);
281+
check_single_int("%B", "10", 2);
282+
check_single_int("%#B", "0B10011", 19);
303283
}

0 commit comments

Comments
 (0)