diff --git a/doc/index.rst b/doc/index.rst index 6f472bfc..fe6afcab 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -115,7 +115,7 @@ The general form of a *standard format specifier* is: sign: "+" | "-" | " " width: `integer` precision: `integer` | "{" `arg_index` "}" - type: "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "o" | "p" | s" | "x" | "X" + type: "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "o" | "p" | s" | "x" | "X" | "b" | "B" The *fill* character can be any character other than '{' or '}'. The presence of a fill character is signaled by the character following it, which must be @@ -167,9 +167,9 @@ following: The ``'#'`` option causes the "alternate form" to be used for the conversion. The alternate form is defined differently for different types. This option is only valid for integer and floating-point types. -For integers, when octal, or hexadecimal output +For integers, when octal, or hexadecimal, or binary output is used, this option adds the prefix respective ``'0'``, or -``'0x'`` to the output value. For floating-point numbers the +``'0x'``, or ``'0b'`` to the output value. For floating-point numbers the alternate form causes the result of the conversion to always contain a decimal-point character, even if no digits follow it. Normally, a decimal-point character appears in the result of these conversions @@ -235,6 +235,12 @@ The available integer presentation types are: | ``'X'`` | Hex format. Outputs the number in base 16, using | | | upper-case letters for the digits above 9. | +---------+----------------------------------------------------------+ + | ``'b'`` | Binary format. Outputs the number in base 2, using | + | | a lower-case 0b if a prefix is requested. | + +---------+----------------------------------------------------------+ + | ``'B'`` | Binary format. Outputs the number in base 2, using | + | | a upper-case 0B if a prefix is requested. | + +---------+----------------------------------------------------------+ | none | The same as ``'d'``. | +---------+----------------------------------------------------------+ @@ -349,13 +355,13 @@ Replacing ``%+f``, ``%-f``, and ``% f`` and specifying a sign:: Format("{:-f}; {:-f}") << 3.14 << -3.14; // show only the minus -- same as '{:f}; {:f}' // Result: "3.140000; -3.140000" -Replacing ``%x`` and ``%o`` and converting the value to different bases:: +Replacing ``%x`` and ``%o`` and ``%b`` and converting the value to different bases:: - Format("int: {0:d}; hex: {0:x}; oct: {0:o}") << 42; - // Result: "int: 42; hex: 2a; oct: 52" - // with 0x or 0 as prefix: - Format("int: {0:d}; hex: {0:#x}; oct: {0:#o}") << 42; - // Result: "int: 42; hex: 0x2a; oct: 052" + Format("int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}") << 42; + // Result: "int: 42; hex: 2a; oct: 52; bin: 101010" + // with 0x or 0 or 0b as prefix: + Format("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}") << 42; + // Result: "int: 42; hex: 0x2a; oct: 052; bin: 0b101010" .. ifconfig:: False diff --git a/format.h b/format.h index 8dda2130..13642b12 100644 --- a/format.h +++ b/format.h @@ -369,6 +369,16 @@ class IntFormatter : public SpecT { T value() const { return value_; } }; +/** + Returns an integer formatter that formats the value in base 2. + */ +IntFormatter > bin(int value); + +/** + Returns an integer formatter that formats the value in base 2. + */ +IntFormatter > binu(int value); + /** Returns an integer formatter that formats the value in base 8. */ @@ -403,6 +413,12 @@ IntFormatter > pad( int value, unsigned width, wchar_t fill = ' '); #define DEFINE_INT_FORMATTERS(TYPE) \ +inline IntFormatter > bin(TYPE value) { \ + return IntFormatter >(value, TypeSpec<'b'>()); \ +} \ +inline IntFormatter > binu(TYPE value) { \ + return IntFormatter >(value, TypeSpec<'B'>()); \ +} \ inline IntFormatter > oct(TYPE value) { \ return IntFormatter >(value, TypeSpec<'o'>()); \ } \ @@ -718,6 +734,25 @@ BasicWriter &BasicWriter::operator<<( } break; } + case 'b': case 'B': { + UnsignedType n = abs_value; + bool print_prefix = f.hash_flag(); + if (print_prefix) size += 2; + do { + ++size; + } while ((n >>= 1) != 0); + Char *p = GetBase(PrepareFilledBuffer(size, f, sign)); + n = abs_value; + const char *digits = "01"; + do { + *p-- = digits[n & 0x1]; + } while ((n >>= 1) != 0); + if (print_prefix) { + *p-- = f.type(); + *p = '0'; + } + break; + } case 'o': { UnsignedType n = abs_value; bool print_prefix = f.hash_flag(); @@ -1155,8 +1190,8 @@ class FormatInt { } explicit FormatInt(unsigned value) : str_(FormatDecimal(value)) {} - const char *c_str() const { return str_; } - std::string str() const { return str_; } + inline const char *c_str() const { return str_; } + inline std::string str() const { return str_; } }; /** diff --git a/format_test.cc b/format_test.cc index 0d4ce80f..9d279e63 100644 --- a/format_test.cc +++ b/format_test.cc @@ -325,6 +325,15 @@ TEST(WriterTest, oct) { EXPECT_EQ("70", str(Writer() << oct(070ul))); } +TEST(WriterTest, bin) { + using fmt::bin; + EXPECT_EQ("1100101011111110", str(Writer() << bin(0xcafe))); + EXPECT_EQ("1011101010111110", str(Writer() << bin(0xbabeu))); + EXPECT_EQ("1101111010101101", str(Writer() << bin(0xdeadl))); + EXPECT_EQ("1011111011101111", str(Writer() << bin(0xbeeful))); + EXPECT_EQ("1111111111111111111111111111111111111111111111111111111111111111", str(Writer() << bin(0xffffffffffffffffull))); +} + TEST(WriterTest, hex) { using fmt::hex; fmt::IntFormatter > (*phex)(int value) = hex; @@ -336,6 +345,8 @@ TEST(WriterTest, hex) { EXPECT_EQ("babe", str(Writer() << hex(0xbabeu))); EXPECT_EQ("dead", str(Writer() << hex(0xdeadl))); EXPECT_EQ("beef", str(Writer() << hex(0xbeeful))); + EXPECT_EQ("beefbeefbeefbeef", str(Writer() << hex(0xbeefbeefbeefbeefull))); + EXPECT_EQ("ffffffffffffffff", str(Writer() << hex(0xffffffffffffffffull))); } TEST(WriterTest, hexu) { @@ -344,6 +355,7 @@ TEST(WriterTest, hexu) { EXPECT_EQ("BABE", str(Writer() << hexu(0xbabeu))); EXPECT_EQ("DEAD", str(Writer() << hexu(0xdeadl))); EXPECT_EQ("BEEF", str(Writer() << hexu(0xbeeful))); + EXPECT_EQ("FFFFFFFFFFFFFFFF", str(Writer() << hexu(0xffffffffffffffffull))); } class Date { @@ -931,7 +943,7 @@ TEST(FormatterTest, FormatShort) { TEST(FormatterTest, FormatInt) { EXPECT_THROW_MSG(Format("{0:v") << 42, FormatError, "unmatched '{' in format"); - CheckUnknownTypes(42, "doxX", "integer"); + CheckUnknownTypes(42, "doxXbB", "integer"); } TEST(FormatterTest, FormatDec) { @@ -966,6 +978,9 @@ TEST(FormatterTest, FormatHex) { EXPECT_EQ("90abcdef", str(Format("{0:x}") << 0x90abcdef)); EXPECT_EQ("12345678", str(Format("{0:X}") << 0x12345678)); EXPECT_EQ("90ABCDEF", str(Format("{0:X}") << 0x90ABCDEF)); + EXPECT_EQ("10010001101000101011001111000", str(Format("{0:b}") << 0x12345678)); + EXPECT_EQ("10010000101010111100110111101111", str(Format("{0:B}") << 0x90ABCDEF)); + char buffer[BUFFER_SIZE]; SPrintf(buffer, "-%x", 0 - static_cast(INT_MIN)); EXPECT_EQ(buffer, str(Format("{0:x}") << INT_MIN)); @@ -979,6 +994,8 @@ TEST(FormatterTest, FormatHex) { EXPECT_EQ(buffer, str(Format("{0:x}") << LONG_MAX)); SPrintf(buffer, "%lx", ULONG_MAX); EXPECT_EQ(buffer, str(Format("{0:x}") << ULONG_MAX)); + SPrintf(buffer, "%llx", ULLONG_MAX); + EXPECT_EQ(buffer, str(Format("{0:x}") << ULLONG_MAX)); } TEST(FormatterTest, FormatOct) {