Compare commits
2 Commits
313ca5e981
...
fa0fef37db
| Author | SHA1 | Date | |
|---|---|---|---|
| fa0fef37db | |||
| c3eb1e2909 |
@ -42,12 +42,12 @@ constexpr inline void check_fmt_params() {
|
||||
|
||||
template <fmt_data_t fmt_data, std::integral arg_t>
|
||||
constexpr inline void format_arg(char* dest, arg_t arg) {
|
||||
const_fmt_detail::format_int<arg_t, fmt_data>(dest, arg);
|
||||
const_fmt_detail::format_int<fmt_data>(dest, arg);
|
||||
};
|
||||
|
||||
template <fmt_data_t fmt_data, std::floating_point arg_t>
|
||||
constexpr inline void format_arg(char* dest, arg_t arg){
|
||||
const_fmt_detail::format_float<arg_t, fmt_data>(dest, arg);
|
||||
const_fmt_detail::format_float<fmt_data>(dest, arg);
|
||||
};
|
||||
|
||||
// TODO: Error handling
|
||||
|
||||
@ -58,22 +58,40 @@ inline int do_count_digits(uint64_t n) {
|
||||
return t - (n < zero_or_powers_of_10[t]);
|
||||
}
|
||||
|
||||
constexpr inline auto count_digits(uint64_t n) -> int {
|
||||
template <FormatType t_format_type>
|
||||
constexpr inline auto count_digits_base(uint64_t n) -> int {
|
||||
if constexpr (t_format_type == FormatType::b) {
|
||||
int result = 0;
|
||||
|
||||
while (n) {
|
||||
n = n >> 1;
|
||||
result += 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
} else {
|
||||
if (!std::is_constant_evaluated()) {
|
||||
return do_count_digits(n);
|
||||
}
|
||||
|
||||
return count_digits_fallback(n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Converts value in the range [0, 100) to a string.
|
||||
constexpr inline const char* digits2(size_t value) {
|
||||
// Converts value in the range [0, base^2) to a string.
|
||||
template <FormatType t_format_type>
|
||||
constexpr inline const char* digits2_base(size_t value) {
|
||||
// GCC generates slightly better code when value is pointer-size.
|
||||
if constexpr (t_format_type == FormatType::b) {
|
||||
return &"00011011"[value * 2];
|
||||
} else {
|
||||
return &"0001020304050607080910111213141516171819"
|
||||
"2021222324252627282930313233343536373839"
|
||||
"4041424344454647484950515253545556575859"
|
||||
"6061626364656667686970717273747576777879"
|
||||
"8081828384858687888990919293949596979899"[value * 2];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -86,9 +104,13 @@ constexpr inline void copy2(char* dst, const char* src) {
|
||||
*dst = static_cast<char>(*src);
|
||||
}
|
||||
|
||||
template <typename uint_t>
|
||||
constexpr inline void format_decimal(char* out, uint_t value, int n_digits,
|
||||
|
||||
template <FormatType t_format_type, typename uint_t>
|
||||
constexpr inline void format_base(char* out, uint_t value, int n_digits,
|
||||
int size) {
|
||||
constexpr unsigned divisor = (t_format_type == FormatType::b) ? 2 : 10;
|
||||
constexpr unsigned square_divisor = const_pow(divisor, 2);
|
||||
|
||||
if (n_digits > size) {
|
||||
for (int i = 0; i < size; ++i) {
|
||||
*(out++) = 'f';
|
||||
@ -97,19 +119,20 @@ constexpr inline void format_decimal(char* out, uint_t value, int n_digits,
|
||||
}
|
||||
|
||||
out += size;
|
||||
while (value >= 100) {
|
||||
while (value >= square_divisor) {
|
||||
out -= 2;
|
||||
copy2(out, digits2(static_cast<size_t>(value % 100)));
|
||||
value /= 100;
|
||||
copy2(out, digits2_base<t_format_type>(
|
||||
static_cast<size_t>(value % square_divisor)));
|
||||
value /= square_divisor;
|
||||
}
|
||||
|
||||
if (value < 10) {
|
||||
if (value < divisor) {
|
||||
*--out = static_cast<char>('0' + value);
|
||||
return;
|
||||
}
|
||||
|
||||
out -= 2;
|
||||
copy2(out, digits2(static_cast<size_t>(value)));
|
||||
copy2(out, digits2_base<t_format_type>(static_cast<size_t>(value)));
|
||||
}
|
||||
|
||||
// returns {abs_value, was_negative}
|
||||
@ -139,18 +162,21 @@ constexpr std::pair<int_t, bool> get_abs_value(int_t value) {
|
||||
*/
|
||||
|
||||
|
||||
template <std::unsigned_integral uint_t, fmt_data_t t_fmt_node>
|
||||
template <fmt_data_t t_fmt_node, std::unsigned_integral uint_t>
|
||||
constexpr inline void format_int(char* out, uint_t value) {
|
||||
format_decimal(out, value, count_digits(value), t_fmt_node.length);
|
||||
// format_decimal(out, value, count_digits(value), t_fmt_node.length);
|
||||
format_base<t_fmt_node.type>(out, value, count_digits_base<t_fmt_node.type>(value),
|
||||
t_fmt_node.length);
|
||||
}
|
||||
|
||||
template <std::signed_integral int_t, fmt_data_t t_fmt_node>
|
||||
|
||||
template <fmt_data_t t_fmt_node, std::signed_integral int_t>
|
||||
constexpr inline void format_int(char* out, int_t value) {
|
||||
const auto [abs_value, negative] = get_abs_value(value);
|
||||
|
||||
const std::size_t n_digits = count_digits(abs_value);
|
||||
const std::size_t n_digits = count_digits_base<t_fmt_node.type>(abs_value);
|
||||
|
||||
format_decimal(out + 1 * (negative), abs_value, n_digits,
|
||||
format_base<t_fmt_node.type>(out + 1 * (negative), abs_value, n_digits,
|
||||
t_fmt_node.length - 1 * (negative));
|
||||
|
||||
if constexpr (t_fmt_node.has_zero_padding) {
|
||||
@ -169,21 +195,21 @@ constexpr inline void format_int(char* out, int_t value) {
|
||||
*/
|
||||
|
||||
|
||||
template <std::floating_point float_t, fmt_data_t t_fmt_node>
|
||||
template <fmt_data_t t_fmt_node, std::floating_point float_t>
|
||||
constexpr inline void format_float(char* out, float_t value) {
|
||||
// clang-format off
|
||||
constexpr fmt_data_t fmt_node_integral = {
|
||||
t_fmt_node.has_zero_padding, // has_zero_padding
|
||||
t_fmt_node.length - t_fmt_node.precision - 1, // length
|
||||
t_fmt_node.precision, // ignored
|
||||
t_fmt_node.type, // ignored
|
||||
FormatType::d, // type
|
||||
t_fmt_node.position // ignored
|
||||
};
|
||||
constexpr fmt_data_t fmt_node_fractional = {
|
||||
true, // has_zero_padding
|
||||
t_fmt_node.precision, // length
|
||||
t_fmt_node.precision, // ignored
|
||||
t_fmt_node.type, // ignored
|
||||
FormatType::d, // type
|
||||
t_fmt_node.position // ignored
|
||||
};
|
||||
// clang-format on
|
||||
@ -198,8 +224,8 @@ constexpr inline void format_float(char* out, float_t value) {
|
||||
const auto [fractional_abs, fractional_negative] =
|
||||
get_abs_value(fractional);
|
||||
|
||||
format_int<int, fmt_node_integral>(out, integral);
|
||||
format_int<uint16_t, fmt_node_fractional>(
|
||||
format_int<fmt_node_integral, int>(out, integral);
|
||||
format_int<fmt_node_fractional, uint16_t>(
|
||||
out + t_fmt_node.length - t_fmt_node.precision, fractional_abs);
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
using namespace const_fmt;
|
||||
|
||||
int main() {
|
||||
constexpr auto s = "This is an integer: {:08}, and this is a float: {:09.4}"_const_fmt(12345, -86.2);
|
||||
constexpr auto s = "This is an integer: {:08b}, and this is a float: {:09.4b}"_const_fmt(125u, -86.2);
|
||||
|
||||
// Convert s (with a type of 'std::array<char, N>') into something
|
||||
// writable to std::cout
|
||||
|
||||
@ -25,5 +25,6 @@ endmacro()
|
||||
|
||||
package_add_test(utility_test src/utility.cpp)
|
||||
package_add_test(parse_test src/parse.cpp)
|
||||
package_add_test(format_test src/format.cpp)
|
||||
package_add_test(format_decimal_test src/format_decimal.cpp)
|
||||
package_add_test(format_binary_test src/format_binary.cpp)
|
||||
|
||||
|
||||
59
test/src/format_binary.cpp
Normal file
59
test/src/format_binary.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
#include <const_fmt/format.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
using namespace const_fmt;
|
||||
using namespace const_fmt::const_fmt_detail;
|
||||
|
||||
|
||||
TEST(FormatBinary, positive_int) {
|
||||
constexpr std::array<char, 8> control1 = {'0', '0', '0', '0',
|
||||
'0', '0', '1', '0'};
|
||||
constexpr std::array<char, 8> formatted1 = const_format<"{:08b}">(0b10);
|
||||
|
||||
constexpr std::array<char, 8> control2 = {' ', ' ', '1', '0',
|
||||
'1', '0', '1', '0'};
|
||||
constexpr std::array<char, 8> formatted2 = const_format<"{:8b}">(0b101010);
|
||||
|
||||
constexpr std::array<char, 8> control3 = {'0', '0', '0', '1',
|
||||
'1', '0', '0', '1'};
|
||||
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4b}">(0b11001);
|
||||
|
||||
constexpr std::array<char, 4> control4 = {'1', '0', '1', '1'};
|
||||
constexpr std::array<char, 4> formatted4 = const_format<"{:4b}">(0b1011);
|
||||
|
||||
constexpr std::array<char, 4> control5 = {'f', 'f', 'f', 'f'};
|
||||
constexpr std::array<char, 4> formatted5 = const_format<"{:4b}">(0b10011);
|
||||
|
||||
EXPECT_EQ(control1, formatted1);
|
||||
EXPECT_EQ(control2, formatted2);
|
||||
EXPECT_EQ(control3, formatted3);
|
||||
EXPECT_EQ(control4, formatted4);
|
||||
EXPECT_EQ(control5, formatted5);
|
||||
}
|
||||
|
||||
TEST(FormatBinary, negative_int) {
|
||||
constexpr std::array<char, 8> control1 = {'-', '0', '0', '0',
|
||||
'0', '0', '1', '0'};
|
||||
constexpr std::array<char, 8> formatted1 = const_format<"{:08b}">(-0b10);
|
||||
|
||||
constexpr std::array<char, 8> control2 = {' ', '-', '1', '0',
|
||||
'1', '0', '1', '0'};
|
||||
constexpr std::array<char, 8> formatted2 = const_format<"{:8b}">(-0b101010);
|
||||
|
||||
constexpr std::array<char, 8> control3 = {'-', '0', '0', '1',
|
||||
'0', '0', '1', '1'};
|
||||
constexpr std::array<char, 8> formatted3 = const_format<"{:08.4b}">(-0b10011);
|
||||
|
||||
constexpr std::array<char, 5> control4 = {'-', '1', '1', '0', '1'};
|
||||
constexpr std::array<char, 5> formatted4 = const_format<"{:5b}">(-0b1101);
|
||||
|
||||
constexpr std::array<char, 5> control5 = {'-', 'f', 'f', 'f', 'f'};
|
||||
constexpr std::array<char, 5> formatted5 = const_format<"{:05b}">(-0b10101);
|
||||
|
||||
EXPECT_EQ(control1, formatted1);
|
||||
EXPECT_EQ(control2, formatted2);
|
||||
EXPECT_EQ(control3, formatted3);
|
||||
EXPECT_EQ(control4, formatted4);
|
||||
EXPECT_EQ(control5, formatted5);
|
||||
}
|
||||
@ -6,7 +6,7 @@ using namespace const_fmt;
|
||||
using namespace const_fmt::const_fmt_detail;
|
||||
|
||||
|
||||
TEST(Format, positive_int) {
|
||||
TEST(FormatDecimal, positive_int) {
|
||||
constexpr std::array<char, 8> control1 = {'0', '0', '0', '0',
|
||||
'0', '0', '0', '2'};
|
||||
constexpr std::array<char, 8> formatted1 = const_format<"{:08}">(2);
|
||||
@ -32,7 +32,7 @@ TEST(Format, positive_int) {
|
||||
EXPECT_EQ(control5, formatted5);
|
||||
}
|
||||
|
||||
TEST(Format, negative_int) {
|
||||
TEST(FormatDecimal, negative_int) {
|
||||
constexpr std::array<char, 8> control1 = {'-', '0', '0', '0',
|
||||
'0', '0', '0', '2'};
|
||||
constexpr std::array<char, 8> formatted1 = const_format<"{:08}">(-2);
|
||||
@ -58,7 +58,7 @@ TEST(Format, negative_int) {
|
||||
EXPECT_EQ(control5, formatted5);
|
||||
}
|
||||
|
||||
TEST(Format, positive_float) {
|
||||
TEST(FormatDecimal, positive_float) {
|
||||
constexpr std::array<char, 8> control1 = {'0', '0', '2', '.',
|
||||
'3', '4', '5', '6'};
|
||||
constexpr std::array<char, 8> formatted1 = const_format<"{:08.4}">(2.3456);
|
||||
@ -82,7 +82,7 @@ TEST(Format, positive_float) {
|
||||
EXPECT_EQ(control4, formatted4);
|
||||
}
|
||||
|
||||
TEST(Format, negative_float) {
|
||||
TEST(FormatDecimal, negative_float) {
|
||||
constexpr std::array<char, 8> control1 = {'-', '0', '2', '.',
|
||||
'3', '4', '5', '6'};
|
||||
constexpr std::array<char, 8> formatted1 = const_format<"{:08.4}">(-2.3456);
|
||||
@ -101,8 +101,3 @@ TEST(Format, negative_float) {
|
||||
EXPECT_EQ(control2, formatted2);
|
||||
EXPECT_EQ(control3, formatted3);
|
||||
}
|
||||
//
|
||||
// TEST(Format, string) {
|
||||
// // TODO
|
||||
// EXPECT_EQ(true, false);
|
||||
//}
|
||||
Loading…
Reference in New Issue
Block a user