mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
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.
This commit is contained in:
parent
518eabadf5
commit
333c3d1f0a
2 changed files with 95 additions and 114 deletions
|
@ -1090,9 +1090,10 @@ int __fmt(void *fn, void *arg, const char *format, va_list va, int *wrote) {
|
||||||
case 'x':
|
case 'x':
|
||||||
log2base = 4;
|
log2base = 4;
|
||||||
goto FormatNumber;
|
goto FormatNumber;
|
||||||
|
case 'B':
|
||||||
case 'b':
|
case 'b':
|
||||||
log2base = 1;
|
log2base = 1;
|
||||||
alphabet = "0123456789abcdefpb";
|
alphabet = (d == 'b' ? "0123456789abcdefpb" : "0123456789ABCDEFPB");
|
||||||
goto FormatNumber;
|
goto FormatNumber;
|
||||||
case 'o':
|
case 'o':
|
||||||
log2base = 3;
|
log2base = 3;
|
||||||
|
|
|
@ -21,6 +21,70 @@
|
||||||
#include "libc/str/str.h"
|
#include "libc/str/str.h"
|
||||||
#include "libc/testlib/testlib.h"
|
#include "libc/testlib/testlib.h"
|
||||||
|
|
||||||
|
static void check_single_double(const char *fmt, const char *expected_str,
|
||||||
|
double value) {
|
||||||
|
char buf[30] = {0};
|
||||||
|
int i = snprintf(buf, sizeof(buf), fmt, value);
|
||||||
|
|
||||||
|
ASSERT_GE(sizeof(buf), strlen(expected_str));
|
||||||
|
ASSERT_EQ(strlen(expected_str), i);
|
||||||
|
ASSERT_STREQ(expected_str, buf);
|
||||||
|
while (i < sizeof(buf))
|
||||||
|
ASSERT_EQ('\0', buf[i++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_single_long_double(const char *fmt, const char *expected_str,
|
||||||
|
long double value) {
|
||||||
|
char buf[30] = {0};
|
||||||
|
int i = snprintf(buf, sizeof(buf), fmt, value);
|
||||||
|
|
||||||
|
ASSERT_GE(sizeof(buf), strlen(expected_str));
|
||||||
|
ASSERT_EQ(strlen(expected_str), i);
|
||||||
|
ASSERT_STREQ(expected_str, buf);
|
||||||
|
while (i < sizeof(buf))
|
||||||
|
ASSERT_EQ('\0', buf[i++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_single_long_double_arr_allowed(
|
||||||
|
const char *fmt, const char *allowed_strs[], long double value) {
|
||||||
|
char buf[30] = {0};
|
||||||
|
int res = snprintf(buf, sizeof(buf), fmt, value);
|
||||||
|
|
||||||
|
for (size_t i = 0; allowed_strs[i] != NULL; ++i)
|
||||||
|
if (strlen(allowed_strs[i]) == res && strcmp(allowed_strs[i], buf) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
printf("Failed to find matching str for %`'s, allowed strs:\n", buf);
|
||||||
|
for (size_t i = 0; allowed_strs[i] != NULL; ++i)
|
||||||
|
printf("- %`'s\n", allowed_strs[i]);
|
||||||
|
fflush(stdout);
|
||||||
|
ASSERT_EQ(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_single_int(const char *fmt, const char *expected_str,
|
||||||
|
int value) {
|
||||||
|
char buf[30] = {0};
|
||||||
|
int i = snprintf(buf, sizeof(buf), fmt, value);
|
||||||
|
|
||||||
|
ASSERT_GE(sizeof(buf), strlen(expected_str));
|
||||||
|
ASSERT_EQ(strlen(expected_str), i);
|
||||||
|
ASSERT_STREQ(expected_str, buf);
|
||||||
|
while (i < sizeof(buf))
|
||||||
|
ASSERT_EQ('\0', buf[i++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_single_wint_t(const char *fmt, const char *expected_str,
|
||||||
|
wint_t value) {
|
||||||
|
char buf[30] = {0};
|
||||||
|
int i = snprintf(buf, sizeof(buf), fmt, value);
|
||||||
|
|
||||||
|
ASSERT_GE(sizeof(buf), strlen(expected_str));
|
||||||
|
ASSERT_EQ(strlen(expected_str), i);
|
||||||
|
ASSERT_STREQ(expected_str, buf);
|
||||||
|
while (i < sizeof(buf))
|
||||||
|
ASSERT_EQ('\0', buf[i++]);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(snprintf, testVeryLargePrecision) {
|
TEST(snprintf, testVeryLargePrecision) {
|
||||||
char buf[512] = {};
|
char buf[512] = {};
|
||||||
int i = snprintf(buf, sizeof(buf), "%.9999u", 10);
|
int i = snprintf(buf, sizeof(buf), "%.9999u", 10);
|
||||||
|
@ -30,59 +94,21 @@ TEST(snprintf, testVeryLargePrecision) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(snprintf, testPlusFlagOnChar) {
|
TEST(snprintf, testPlusFlagOnChar) {
|
||||||
char buf[10] = {};
|
check_single_int("%+c", "=", '=');
|
||||||
int i = snprintf(buf, sizeof(buf), "%+c", '=');
|
|
||||||
|
|
||||||
ASSERT_EQ(1, i);
|
|
||||||
ASSERT_STREQ("=", buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(snprintf, testInf) {
|
TEST(snprintf, testInf) {
|
||||||
char buf[10] = {};
|
check_single_double("%f", "inf", 1.0 / 0.0);
|
||||||
int i = snprintf(buf, sizeof(buf), "%f", 1.0 / 0.0);
|
check_single_long_double("%Lf", "inf", 1.0L / 0.0L);
|
||||||
|
check_single_double("%e", "inf", 1.0 / 0.0);
|
||||||
ASSERT_EQ(3, i);
|
check_single_long_double("%Le", "inf", 1.0L / 0.0L);
|
||||||
ASSERT_STREQ("inf", buf);
|
check_single_double("%g", "inf", 1.0 / 0.0);
|
||||||
|
check_single_long_double("%Lg", "inf", 1.0L / 0.0L);
|
||||||
memset(buf, '\0', 4);
|
|
||||||
i = snprintf(buf, sizeof(buf), "%Lf", 1.0L / 0.0L);
|
|
||||||
ASSERT_EQ(3, i);
|
|
||||||
ASSERT_STREQ("inf", buf);
|
|
||||||
|
|
||||||
memset(buf, '\0', 4);
|
|
||||||
i = snprintf(buf, sizeof(buf), "%e", 1.0 / 0.0);
|
|
||||||
ASSERT_EQ(3, i);
|
|
||||||
ASSERT_STREQ("inf", buf);
|
|
||||||
|
|
||||||
memset(buf, '\0', 4);
|
|
||||||
i = snprintf(buf, sizeof(buf), "%Le", 1.0L / 0.0L);
|
|
||||||
ASSERT_EQ(3, i);
|
|
||||||
ASSERT_STREQ("inf", buf);
|
|
||||||
|
|
||||||
memset(buf, '\0', 4);
|
|
||||||
i = snprintf(buf, sizeof(buf), "%g", 1.0 / 0.0);
|
|
||||||
ASSERT_EQ(3, i);
|
|
||||||
ASSERT_STREQ("inf", buf);
|
|
||||||
|
|
||||||
memset(buf, '\0', 4);
|
|
||||||
i = snprintf(buf, sizeof(buf), "%Lg", 1.0L / 0.0L);
|
|
||||||
ASSERT_EQ(3, i);
|
|
||||||
ASSERT_STREQ("inf", buf);
|
|
||||||
|
|
||||||
for (i = 4; i < 10; ++i)
|
|
||||||
ASSERT_EQ('\0', buf[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(snprintf, testUppercaseCConversionSpecifier) {
|
TEST(snprintf, testUppercaseCConversionSpecifier) {
|
||||||
char buf[10] = {};
|
check_single_wint_t("%C", "a", L'a');
|
||||||
int i = snprintf(buf, sizeof(buf), "%C", L'a');
|
check_single_wint_t("%C", "☺", L'☺');
|
||||||
|
|
||||||
ASSERT_EQ(1, i);
|
|
||||||
ASSERT_STREQ("a", buf);
|
|
||||||
|
|
||||||
i = snprintf(buf, sizeof(buf), "%C", L'☺');
|
|
||||||
ASSERT_EQ(3, i);
|
|
||||||
ASSERT_STREQ("☺", buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we don't va_arg the wrong argument size on wide character
|
// Make sure we don't va_arg the wrong argument size on wide character
|
||||||
|
@ -188,74 +214,26 @@ TEST(snprintf, testNConversionSpecifier) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(snprintf, testLongDoubleEConversionSpecifier) {
|
TEST(snprintf, testLongDoubleEConversionSpecifier) {
|
||||||
char buf[20] = {};
|
check_single_long_double("%Le", "1.234568e+06", 1234567.8L);
|
||||||
int i = snprintf(buf, sizeof(buf), "%Le", 1234567.8L);
|
|
||||||
|
|
||||||
ASSERT_EQ(12, i);
|
|
||||||
ASSERT_STREQ("1.234568e+06", buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(snprintf, testLongDoubleRounding) {
|
TEST(snprintf, testLongDoubleRounding) {
|
||||||
int previous_rounding = fegetround();
|
int previous_rounding = fegetround();
|
||||||
ASSERT_EQ(0, fesetround(FE_DOWNWARD));
|
ASSERT_EQ(0, fesetround(FE_DOWNWARD));
|
||||||
|
|
||||||
char buf[20];
|
check_single_long_double("%.3Lf", "4.437", 4.4375L);
|
||||||
int i = snprintf(buf, sizeof(buf), "%.3Lf", 4.4375L);
|
check_single_long_double("%.3Lf", "-4.438", -4.4375L);
|
||||||
ASSERT_EQ(5, i);
|
|
||||||
ASSERT_STREQ("4.437", buf);
|
|
||||||
|
|
||||||
i = snprintf(buf, sizeof(buf), "%.3Lf", -4.4375L);
|
|
||||||
ASSERT_EQ(6, i);
|
|
||||||
ASSERT_STREQ("-4.438", buf);
|
|
||||||
|
|
||||||
ASSERT_EQ(0, fesetround(FE_TOWARDZERO));
|
ASSERT_EQ(0, fesetround(FE_TOWARDZERO));
|
||||||
|
|
||||||
i = snprintf(buf, sizeof(buf), "%.3Lf", -4.4375L);
|
check_single_long_double("%.3Lf", "-4.437", -4.4375L);
|
||||||
ASSERT_EQ(6, i);
|
|
||||||
ASSERT_STREQ("-4.437", buf);
|
|
||||||
|
|
||||||
ASSERT_EQ(0, fesetround(previous_rounding));
|
ASSERT_EQ(0, fesetround(previous_rounding));
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_a_conversion_specifier_double(const char *fmt,
|
|
||||||
const char *expected_str,
|
|
||||||
double value) {
|
|
||||||
char buf[30] = {0};
|
|
||||||
int i = snprintf(buf, sizeof(buf), fmt, value);
|
|
||||||
|
|
||||||
ASSERT_EQ(strlen(expected_str), i);
|
|
||||||
ASSERT_STREQ(expected_str, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void check_a_conversion_specifier_long_double(const char *fmt,
|
|
||||||
const char *expected_str,
|
|
||||||
long double value) {
|
|
||||||
char buf[30] = {0};
|
|
||||||
int i = snprintf(buf, sizeof(buf), fmt, value);
|
|
||||||
|
|
||||||
ASSERT_EQ(strlen(expected_str), i);
|
|
||||||
ASSERT_STREQ(expected_str, buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void check_a_conversion_specifier_long_double_arr_allowed(
|
|
||||||
const char *fmt, const char *allowed_strs[], long double value) {
|
|
||||||
char buf[30] = {0};
|
|
||||||
int res = snprintf(buf, sizeof(buf), fmt, value);
|
|
||||||
|
|
||||||
for (size_t i = 0; allowed_strs[i] != NULL; ++i)
|
|
||||||
if (strlen(allowed_strs[i]) == res && strcmp(allowed_strs[i], buf) == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
printf("Failed to find matching str for %`'s, allowed strs:\n", buf);
|
|
||||||
for (size_t i = 0; allowed_strs[i] != NULL; ++i)
|
|
||||||
printf("- %`'s\n", allowed_strs[i]);
|
|
||||||
fflush(stdout);
|
|
||||||
ASSERT_EQ(false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void check_a_conversion_specifier_double_prec_1(const char *expected_str,
|
void check_a_conversion_specifier_double_prec_1(const char *expected_str,
|
||||||
double value) {
|
double value) {
|
||||||
check_a_conversion_specifier_double("%.1a", expected_str, value);
|
check_single_double("%.1a", expected_str, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(snprintf, testAConversionSpecifierRounding) {
|
TEST(snprintf, testAConversionSpecifierRounding) {
|
||||||
|
@ -281,23 +259,25 @@ TEST(snprintf, testAConversionSpecifier) {
|
||||||
check_a_conversion_specifier_double_prec_1("0x1.ap+4", 0x1.98p+4);
|
check_a_conversion_specifier_double_prec_1("0x1.ap+4", 0x1.98p+4);
|
||||||
check_a_conversion_specifier_double_prec_1("0x1.ap+4", 0x1.a8p+4);
|
check_a_conversion_specifier_double_prec_1("0x1.ap+4", 0x1.a8p+4);
|
||||||
|
|
||||||
check_a_conversion_specifier_double("%#a", "0x0.p+0", 0x0.0p0);
|
check_single_double("%#a", "0x0.p+0", 0x0.0p0);
|
||||||
check_a_conversion_specifier_double("%#A", "0X0.P+0", 0x0.0p0);
|
check_single_double("%#A", "0X0.P+0", 0x0.0p0);
|
||||||
check_a_conversion_specifier_long_double("%#La", "0x0.p+0", 0x0.0p0L);
|
check_single_long_double("%#La", "0x0.p+0", 0x0.0p0L);
|
||||||
check_a_conversion_specifier_long_double("%#LA", "0X0.P+0", 0x0.0p0L);
|
check_single_long_double("%#LA", "0X0.P+0", 0x0.0p0L);
|
||||||
|
|
||||||
check_a_conversion_specifier_double("%.2a", "0x1.00p-1026", 0xf.fffp-1030);
|
check_single_double("%.2a", "0x1.00p-1026", 0xf.fffp-1030);
|
||||||
|
|
||||||
check_a_conversion_specifier_double("%.1a", "0x2.0p+0", 1.999);
|
|
||||||
|
|
||||||
|
check_single_double("%.1a", "0x2.0p+0", 1.999);
|
||||||
const char *acceptable_results1[] = {"0x1.0p+1", "0x2.0p+0", NULL};
|
const char *acceptable_results1[] = {"0x1.0p+1", "0x2.0p+0", NULL};
|
||||||
check_a_conversion_specifier_long_double_arr_allowed(
|
check_single_long_double_arr_allowed(
|
||||||
"%.1La", acceptable_results1, 1.999L);
|
"%.1La", acceptable_results1, 1.999L);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(snprintf, apostropheFlag) {
|
TEST(snprintf, testApostropheFlag) {
|
||||||
char buf[20];
|
check_single_int("%'d", "10000000", 10000000);
|
||||||
int i = snprintf(buf, sizeof(buf), "%'d", 1000000);
|
}
|
||||||
ASSERT_EQ(7, i);
|
|
||||||
ASSERT_STREQ("1000000", buf);
|
TEST(snprintf, testUppercaseBConversionSpecifier) {
|
||||||
|
check_single_int("%B", "0", 0);
|
||||||
|
check_single_int("%B", "10", 2);
|
||||||
|
check_single_int("%#B", "0B10011", 19);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue