Skip to content

Commit e9b2191

Browse files
committed
Add a 32-bit version of CountDigits.
1 parent 3017fc6 commit e9b2191

File tree

3 files changed

+83
-39
lines changed

3 files changed

+83
-39
lines changed

format-test.cc

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,24 @@ TEST(UtilTest, Increment) {
170170
EXPECT_STREQ("200", s);
171171
}
172172

173+
// Tests fmt::internal::CountDigits for integer type Int.
174+
template <typename Int>
175+
void TestCountDigits(Int) {
176+
for (Int i = 0; i < 10; ++i)
177+
EXPECT_EQ(1, fmt::internal::CountDigits(i));
178+
for (Int i = 1, n = 1,
179+
end = std::numeric_limits<Int>::max() / 10; n <= end; ++i) {
180+
n *= 10;
181+
EXPECT_EQ(i, fmt::internal::CountDigits(n - 1));
182+
EXPECT_EQ(i + 1, fmt::internal::CountDigits(n));
183+
}
184+
}
185+
186+
TEST(UtilTest, CountDigits) {
187+
TestCountDigits(uint32_t());
188+
TestCountDigits(uint64_t());
189+
}
190+
173191
class TestString {
174192
private:
175193
std::string value_;
@@ -1427,18 +1445,21 @@ TEST(FormatIntTest, FormatInt) {
14271445
EXPECT_EQ(os.str(), fmt::FormatInt(std::numeric_limits<int64_t>::max()).str());
14281446
}
14291447

1430-
TEST(FormatIntTest, FormatDec) {
1448+
template <typename T>
1449+
std::string FormatDec(T value) {
14311450
char buffer[10];
14321451
char *ptr = buffer;
1433-
fmt::FormatDec(ptr, 42);
1434-
EXPECT_EQ(buffer + 2, ptr);
1435-
*ptr = '\0';
1436-
EXPECT_STREQ("42", buffer);
1437-
ptr = buffer;
1438-
fmt::FormatDec(ptr, -42);
1439-
*ptr = '\0';
1440-
EXPECT_EQ(buffer + 3, ptr);
1441-
EXPECT_STREQ("-42", buffer);
1452+
fmt::FormatDec(ptr, value);
1453+
return std::string(buffer, ptr);
1454+
}
1455+
1456+
TEST(FormatIntTest, FormatDec) {
1457+
EXPECT_EQ("42", FormatDec(42));
1458+
EXPECT_EQ("-42", FormatDec(-42));
1459+
EXPECT_EQ("42", FormatDec(42l));
1460+
EXPECT_EQ("42", FormatDec(42ul));
1461+
EXPECT_EQ("42", FormatDec(42ll));
1462+
EXPECT_EQ("42", FormatDec(42ull));
14421463
}
14431464

14441465
template <typename T>

format.cc

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -118,27 +118,23 @@ const char fmt::internal::DIGITS[] =
118118
"4041424344454647484950515253545556575859"
119119
"6061626364656667686970717273747576777879"
120120
"8081828384858687888990919293949596979899";
121-
122-
const uint64_t fmt::internal::POWERS_OF_10[] = {
121+
122+
#define FMT_POWERS_OF_10(prefix) \
123+
prefix 10, \
124+
prefix 100, \
125+
prefix 1000, \
126+
prefix 10000, \
127+
prefix 100000, \
128+
prefix 1000000, \
129+
prefix 10000000, \
130+
prefix 100000000, \
131+
prefix 1000000000
132+
133+
const uint32_t fmt::internal::POWERS_OF_10_32[] = {0, FMT_POWERS_OF_10()};
134+
const uint64_t fmt::internal::POWERS_OF_10_64[] = {
123135
0,
124-
10,
125-
100,
126-
1000,
127-
10000,
128-
100000,
129-
1000000,
130-
10000000,
131-
100000000,
132-
1000000000,
133-
ULongLong(1000000000) * 10,
134-
ULongLong(1000000000) * 100,
135-
ULongLong(1000000000) * 1000,
136-
ULongLong(1000000000) * 10000,
137-
ULongLong(1000000000) * 100000,
138-
ULongLong(1000000000) * 1000000,
139-
ULongLong(1000000000) * 10000000,
140-
ULongLong(1000000000) * 100000000,
141-
ULongLong(1000000000) * 1000000000,
136+
FMT_POWERS_OF_10(),
137+
FMT_POWERS_OF_10(ULongLong(1000000000) *),
142138
ULongLong(1000000000) * ULongLong(1000000000) * 10
143139
};
144140

format.h

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -213,17 +213,32 @@ class CharTraits<wchar_t> : public BasicCharTraits<wchar_t> {
213213
const wchar_t *format, unsigned width, int precision, T value);
214214
};
215215

216+
// Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise.
217+
template <bool FitsIn32Bits>
218+
struct TypeSelector { typedef uint32_t Type; };
219+
220+
template <>
221+
struct TypeSelector<false> { typedef uint64_t Type; };
222+
223+
template <typename T>
224+
struct IntTraitsBase {
225+
// Smallest of uint32_t and uint64_t that is large enough to represent
226+
// all values of T.
227+
typedef typename
228+
TypeSelector<std::numeric_limits<T>::digits <= 32>::Type MainType;
229+
};
230+
216231
// Information about an integer type.
217232
// IntTraits is not specialized for integer types smaller than int,
218233
// since these are promoted to int.
219234
template <typename T>
220-
struct IntTraits {
235+
struct IntTraits : IntTraitsBase<T> {
221236
typedef T UnsignedType;
222237
static bool IsNegative(T) { return false; }
223238
};
224239

225240
template <typename T, typename UnsignedT>
226-
struct SignedIntTraits {
241+
struct SignedIntTraits : IntTraitsBase<T> {
227242
typedef UnsignedT UnsignedType;
228243
static bool IsNegative(T value) { return value < 0; }
229244
};
@@ -245,17 +260,28 @@ struct IsLongDouble<long double> { enum {VALUE = 1}; };
245260

246261
void ReportUnknownType(char code, const char *type);
247262

248-
extern const uint64_t POWERS_OF_10[];
263+
extern const uint32_t POWERS_OF_10_32[];
264+
extern const uint64_t POWERS_OF_10_64[];
249265

266+
#if FMT_GCC_VERSION >= 400 || __has_builtin(__builtin_clzll)
250267
// Returns the number of decimal digits in n. Leading zeros are not counted
251268
// except for n == 0 in which case CountDigits returns 1.
252269
inline unsigned CountDigits(uint64_t n) {
253-
#if FMT_GCC_VERSION >= 400 || __has_builtin(__builtin_clzll)
254270
// Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
255271
// and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits.
256272
uint64_t t = (64 - __builtin_clzll(n | 1)) * 1233 >> 12;
257-
return t - (n < POWERS_OF_10[t]) + 1;
273+
return t - (n < POWERS_OF_10_64[t]) + 1;
274+
}
275+
# if FMT_GCC_VERSION >= 400 || __has_builtin(__builtin_clz)
276+
// Optional version of CountDigits for better performance on 32-bit platforms.
277+
inline unsigned CountDigits(uint32_t n) {
278+
uint32_t t = (32 - __builtin_clz(n | 1)) * 1233 >> 12;
279+
return t - (n < POWERS_OF_10_32[t]) + 1;
280+
}
281+
# endif
258282
#else
283+
// Slower version of CountDigits used when __builtin_clz is not available.
284+
inline unsigned CountDigits(uint64_t n) {
259285
unsigned count = 1;
260286
for (;;) {
261287
// Integer division is slow so do it for a group of four digits instead
@@ -268,8 +294,8 @@ inline unsigned CountDigits(uint64_t n) {
268294
n /= 10000u;
269295
count += 4;
270296
}
271-
#endif
272297
}
298+
#endif
273299

274300
extern const char DIGITS[];
275301

@@ -838,7 +864,7 @@ template <typename T, typename Spec>
838864
void BasicWriter<Char>::FormatInt(T value, const Spec &spec) {
839865
unsigned size = 0;
840866
char sign = 0;
841-
typedef typename internal::IntTraits<T>::UnsignedType UnsignedType;
867+
typedef typename internal::IntTraits<T>::MainType UnsignedType;
842868
UnsignedType abs_value = value;
843869
if (internal::IntTraits<T>::IsNegative(value)) {
844870
sign = '-';
@@ -850,7 +876,8 @@ void BasicWriter<Char>::FormatInt(T value, const Spec &spec) {
850876
}
851877
switch (spec.type()) {
852878
case 0: case 'd': {
853-
unsigned num_digits = internal::CountDigits(abs_value);
879+
typename internal::IntTraits<T>::MainType normalized_value = abs_value;
880+
unsigned num_digits = internal::CountDigits(normalized_value);
854881
CharPtr p =
855882
PrepareFilledBuffer(size + num_digits, spec, sign) + 1 - num_digits;
856883
internal::FormatDecimal(GetBase(p), abs_value, num_digits);
@@ -1380,7 +1407,7 @@ class FormatInt {
13801407
// write a terminating null character.
13811408
template <typename T>
13821409
inline void FormatDec(char *&buffer, T value) {
1383-
typedef typename internal::IntTraits<T>::UnsignedType UnsignedType;
1410+
typedef typename internal::IntTraits<T>::MainType UnsignedType;
13841411
UnsignedType abs_value = value;
13851412
if (internal::IntTraits<T>::IsNegative(value)) {
13861413
*buffer++ = '-';

0 commit comments

Comments
 (0)