Skip to content

Commit

Permalink
Add UIToA<uint64_t>
Browse files Browse the repository at this point in the history
Turns out, using `IToA` for magnitudes was a little bit sloppy.
Magnitude values are always unsigned, which means there are meaningful
values that won't fit inside of an `int64_t`.  Fortunately, adding all
those constants (#336) was a great stress test to expose this.

I think the most natural fix would be to add a `UIToA`, which can't
handle signed values, but which _can_ handle the "upper half" of
`uint64_t` values.  This way, we can have `IToA` delegate to `UIToA` for
the "integer magnitude" parts of the logic.

Why not just get rid of `IToA` altogether?  Well, we do need it for
printing exponents, which can be negative.

Helps #90: this gets the test to pass on #336.
  • Loading branch information
chiphogg committed Dec 2, 2024
1 parent 9b2f9a7 commit 91fc2b7
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 20 deletions.
2 changes: 1 addition & 1 deletion au/code/au/magnitude.hh
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ constexpr const bool MagnitudeLabelImplementation<MagT, Category>::has_exposed_s

template <typename MagT>
struct MagnitudeLabelImplementation<MagT, MagLabelCategory::INTEGER>
: detail::IToA<get_value<std::uintmax_t>(MagT{})> {
: detail::UIToA<get_value<std::uintmax_t>(MagT{})> {
static constexpr const bool has_exposed_slash = false;
};
template <typename MagT>
Expand Down
63 changes: 44 additions & 19 deletions au/code/au/utility/string_constant.hh
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,8 @@ struct IToA;
// Implementation details below.
////////////////////////////////////////////////////////////////////////////////////////////////////

// The string-length needed to hold a representation of this integer.
constexpr std::size_t string_size(int64_t x) {
if (x < 0) {
return string_size(-x) + 1;
}

// The string-length needed to hold a representation of this unsigned integer.
constexpr std::size_t string_size_unsigned(uint64_t x) {
std::size_t digits = 1;
while (x > 9) {
x /= 10;
Expand All @@ -101,6 +97,16 @@ constexpr std::size_t string_size(int64_t x) {
return digits;
}

// The string-length needed to hold a representation of this integer.
constexpr std::size_t string_size(int64_t x) {
std::size_t sign_length = 0u;
if (x < 0) {
x = -x;
++sign_length;
}
return string_size_unsigned(static_cast<uint64_t>(x)) + sign_length;
}

// The sum of the template parameters.
template <std::size_t... Ns>
constexpr std::size_t sum() {
Expand Down Expand Up @@ -201,33 +207,52 @@ constexpr auto join_by(const SepT &sep, const StringTs &...ts) {
return as_string_constant(sep).join(as_string_constant(ts)...);
}

template <int64_t N>
struct IToA {
template <uint64_t N>
struct UIToA {
private:
static constexpr auto print_to_array() {
char data[length + 1] = {'\0'};

int num = N;
if (num < 0) {
data[0] = '-';
num = -num;
}
char data[length + 1u] = {'\0'};

uint64_t num = N;
std::size_t i = length - 1;
do {
data[i--] = '0' + static_cast<char>(num % 10);
num /= 10;
} while (num > 0);
data[i--] = '0' + static_cast<char>(num % 10u);
num /= 10u;
} while (num > 0u);

return StringConstant<length>{data};
}

public:
static constexpr std::size_t length = string_size(N);
static constexpr std::size_t length = string_size_unsigned(N);

static constexpr StringConstant<length> value = print_to_array();
};

// Definitions for UIToA<N>::value. (Needed to prevent linker errors.)
template <uint64_t N>
constexpr std::size_t UIToA<N>::length;
template <uint64_t N>
constexpr StringConstant<UIToA<N>::length> UIToA<N>::value;

template <bool IsPositive>
struct SignIfPositiveIs {
static constexpr StringConstant<0> value() { return ""; }
};
template <>
struct SignIfPositiveIs<false> {
static constexpr StringConstant<1> value() { return "-"; }
};

template <int64_t N>
struct IToA {
static constexpr std::size_t length = string_size(N);

static constexpr StringConstant<length> value =
concatenate(SignIfPositiveIs<(N >= 0)>::value(),
UIToA<static_cast<uint64_t>((N) >= 0) ? N : -N>::value);
};

// Definitions for IToA<N>::value. (Needed to prevent linker errors.)
template <int64_t N>
constexpr std::size_t IToA<N>::length;
Expand Down
4 changes: 4 additions & 0 deletions au/code/au/utility/test/string_constant_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ TEST(IToA, HasLengthMember) {
EXPECT_EQ(IToA<-12345>::length, 6);
}

TEST(UIToA, CanHandleNumbersBiggerThanIntmaxButWithinUintmax) {
EXPECT_STREQ(UIToA<10000000000000000000u>::value.c_str(), "10000000000000000000");
}

TEST(join, EmptyStringForNoArguments) {
constexpr auto x = as_string_constant("sep").join();
EXPECT_STREQ(x.c_str(), "");
Expand Down

0 comments on commit 91fc2b7

Please sign in to comment.