BigInteger implementation, carry discrepancy - c++

lately, I've been trying to make my on Big Integer class. Right now, I've been doing some prototypes, they almost work. This prototype is not a function, nor a class, just a quick stuff I did to see if it worked.
This is my prototype so far: (it looks a bit ugly with all the casts just to please the compiler)
std::vector<long long unsigned> vec1 {4294967295, 2294967295, 1294967295};
std::vector<long long unsigned> vec2 {4294967295, 2294967295, 1294967295};
int carry {};
for (int i {static_cast<int>(vec1.size()) - 1}; i != -1; --i) {
int unsigned greater = static_cast<unsigned int>(std::max(vec1[i], vec2[i]));
int unsigned result {};
if (i < static_cast<int>(vec2.size())) {
result = static_cast<int unsigned>(vec2[i] + vec1[i] + carry);
} else if (carry) {
result = static_cast<int unsigned>(vec1[i] + carry);
} else {
break;
}
if (result <= greater) {
vec1[i] += result;
carry = 1;
} else {
vec1[i] = result;
carry = 0;
}
}
if (carry) {
vec1.back() += 1;
}
for (auto const n : vec1) {
cout << n;
}
And these is the result:
858993459025899345892589934591
^ ^
858993459045899345902589934590 -> the correct one!
So, what I'm doing wrong?
It does give the same result in gcc and visual studio.

As written, if the highest magnitude addition carries, you end up incrementing the lowest magnitude value:
if (carry) {
vec1.back() += 1;
}
Presumably, the correct behavior would be to expand the vector and allow the carry to occupy the new highest value, e.g.:
if (carry) {
vec1.insert(vec1.begin(), 1);
}
This does mean you end up expanding vec1 (and copying all the existing values, because insertion at the beginning of a vector isn't cheap), which might or might not be correct given the design of your class (your addition operation looks unsafe if vec1 and vec2 don't have matching size, so it's unclear whether vec1 is allowed to expand).

This
if (carry) {
vec1.back() += 1;
}
adds one to the last bin.
Perhaps you want to insert one to the front?

Related

How to convert Biginteger to string

I have a vector with digits of number, vector represents big integer in system with base 2^32. For example:
vector <unsigned> vec = {453860625, 469837947, 3503557200, 40}
This vector represent this big integer:
base = 2 ^ 32
3233755723588593872632005090577 = 40 * base ^ 3 + 3503557200 * base ^ 2 + 469837947 * base + 453860625
How to get this decimal representation in string?
Here is an inefficient way to do what you want, get a decimal string from a vector of word values representing an integer of arbitrary size.
I would have preferred to implement this as a class, for better encapsulation and so math operators could be added, but to better comply with the question, this is just a bunch of free functions for manipulating std::vector<unsigned> objects. This does use a typedef BiType as an alias for std::vector<unsigned> however.
Functions for doing the binary division make up most of this code. Much of it duplicates what can be done with std::bitset, but for bitsets of arbitrary size, as vectors of unsigned words. If you want to improve efficiency, plug in a division algorithm which does per-word operations, instead of per-bit. Also, the division code is general-purpose, when it is only ever used to divide by 10, so you could replace it with special-purpose division code.
The code generally assumes a vector of unsigned words and also that the base is the maximum unsigned value, plus one. I left a comment wherever things would go wrong for smaller bases or bases which are not a power of 2 (binary division requires base to be a power of 2).
Also, I only tested for 1 case, the one you gave in the OP -- and this is new, unverified code, so you might want to do some more testing. If you find a problem case, I'll be happy to fix the bug here.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
namespace bigint {
using BiType = std::vector<unsigned>;
// cmp compares a with b, returning 1:a>b, 0:a==b, -1:a<b
int cmp(const BiType& a, const BiType& b) {
const auto max_size = std::max(a.size(), b.size());
for(auto i=max_size-1; i+1; --i) {
const auto wa = i < a.size() ? a[i] : 0;
const auto wb = i < b.size() ? b[i] : 0;
if(wa != wb) { return wa > wb ? 1 : -1; }
}
return 0;
}
bool is_zero(BiType& bi) {
for(auto w : bi) { if(w) return false; }
return true;
}
// canonize removes leading zero words
void canonize(BiType& bi) {
const auto size = bi.size();
if(!size || bi[size-1]) return;
for(auto i=size-2; i+1; --i) {
if(bi[i]) {
bi.resize(i + 1);
return;
}
}
bi.clear();
}
// subfrom subtracts b from a, modifying a
// a >= b must be guaranteed by caller
void subfrom(BiType& a, const BiType& b) {
unsigned borrow = 0;
for(std::size_t i=0; i<b.size(); ++i) {
if(b[i] || borrow) {
// TODO: handle error if i >= a.size()
const auto w = a[i] - b[i] - borrow;
// this relies on the automatic w = w (mod base),
// assuming unsigned max is base-1
// if this is not the case, w must be set to w % base here
borrow = w >= a[i];
a[i] = w;
}
}
for(auto i=b.size(); borrow; ++i) {
// TODO: handle error if i >= a.size()
borrow = !a[i];
--a[i];
// a[i] must be set modulo base here too
// (this is automatic when base is unsigned max + 1)
}
}
// binary division and its helpers: these require base to be a power of 2
// hi_bit_set is base/2
// the definition assumes CHAR_BIT == 8
const auto hi_bit_set = unsigned(1) << (sizeof(unsigned) * 8 - 1);
// shift_right_1 divides bi by 2, truncating any fraction
void shift_right_1(BiType& bi) {
unsigned carry = 0;
for(auto i=bi.size()-1; i+1; --i) {
const auto next_carry = (bi[i] & 1) ? hi_bit_set : 0;
bi[i] >>= 1;
bi[i] |= carry;
carry = next_carry;
}
// if carry is nonzero here, 1/2 was truncated from the result
canonize(bi);
}
// shift_left_1 multiplies bi by 2
void shift_left_1(BiType& bi) {
unsigned carry = 0;
for(std::size_t i=0; i<bi.size(); ++i) {
const unsigned next_carry = !!(bi[i] & hi_bit_set);
bi[i] <<= 1; // assumes high bit is lost, i.e. base is unsigned max + 1
bi[i] |= carry;
carry = next_carry;
}
if(carry) { bi.push_back(1); }
}
// sets an indexed bit in bi, growing the vector when required
void set_bit_at(BiType& bi, std::size_t index, bool set=true) {
std::size_t widx = index / (sizeof(unsigned) * 8);
std::size_t bidx = index % (sizeof(unsigned) * 8);
if(bi.size() < widx + 1) { bi.resize(widx + 1); }
if(set) { bi[widx] |= unsigned(1) << bidx; }
else { bi[widx] &= ~(unsigned(1) << bidx); }
}
// divide divides n by d, returning the result and leaving the remainder in n
// this is implemented using binary division
BiType divide(BiType& n, BiType d) {
if(is_zero(d)) {
// TODO: handle divide by zero
return {};
}
std::size_t shift = 0;
while(cmp(n, d) == 1) {
shift_left_1(d);
++shift;
}
BiType result;
do {
if(cmp(n, d) >= 0) {
set_bit_at(result, shift);
subfrom(n, d);
}
shift_right_1(d);
} while(shift--);
canonize(result);
canonize(n);
return result;
}
std::string get_decimal(BiType bi) {
std::string dec_string;
// repeat division by 10, using the remainder as a decimal digit
// this will build a string with digits in reverse order, so
// before returning, it will be reversed to correct this.
do {
const auto next_bi = divide(bi, {10});
const char digit_value = static_cast<char>(bi.size() ? bi[0] : 0);
dec_string.push_back('0' + digit_value);
bi = next_bi;
} while(!is_zero(bi));
std::reverse(dec_string.begin(), dec_string.end());
return dec_string;
}
}
int main() {
bigint::BiType my_big_int = {453860625, 469837947, 3503557200, 40};
auto dec_string = bigint::get_decimal(my_big_int);
std::cout << dec_string << '\n';
}
Output:
3233755723588593872632005090577

How can I keep only non-zero digits from an integer?

I am currently using the code below that removes all digits equal to zero from an integer.
int removeZeros(int candid)
{
int output = 0;
string s(itoa(candid));
for (int i = s.size(); i != 0; --i)
{
if (s[i] != '0') output = output * 10 + atoi(s[i]);
}
return output;
}
The expected output for e.g. 102304 would be 1234.
Is there a more compact way of doing this by directly working on the integer, that is, not string representation? Is it actually going to be faster?
Here's a way to do it without strings and buffers.
I've only tested this with positive numbers. To make this work with negative numbers is an exercise left up to you.
int removeZeros(int x)
{
int result = 0;
int multiplier = 1;
while (x > 0)
{
int digit = x % 10;
if (digit != 0)
{
int val = digit * multiplier;
result += val;
multiplier *= 10;
}
x = x / 10;
}
return result;
}
For maintainability, I would suggest, don't work directly on the numeric value. You can express your requirements in a very straightforward way using string manipulations, and while it's true that it will likely perform slower than number manipulations, I expect either to be fast enough that you don't have to worry about the performance unless it's in an extremely tight loop.
int removeZeros(int n) {
auto s = std::to_string(n);
s.erase(std::remove(s.begin(), s.end(), '0'), s.end());
return std::stoi(s);
}
As a bonus, this simpler implementation handles negative numbers correctly. For zero, it throws std::invalid_argument, because removing all zeros from 0 doesn't produce a number.
You could try something like this:
template<typename T> T nozeros( T const & z )
{
return z==0 ? 0 : (z%10?10:1)*nozeros(z/10)+(z%10);
}
If you want to take your processing one step further you can do a nice tail recursion , no need for a helper function:
template<typename T> inline T pow10(T p, T res=1)
{
return p==0 ? res : pow10(--p,res*10);
}
template<typename T> T nozeros( T const & z , T const & r=0, T const & zp =0)
{
static int digit =-1;
return not ( z ^ r ) ? digit=-1, zp : nozeros(z/10,z%10, r ? r*pow10(++digit)+zp : zp);
}
Here is how this will work with input 32040
Ret, z, r, zp, digits
-,32040,0,0, -1
-,3204,0,0, -1
-,320,4,0,0, -1
-,32,0,4,4, 0
-,3,2,4, 0
-,0,3,24, 1
-,0,0,324, 2
324,-,-,-, -1
Integer calculations are always faster than actually transforming your integer to string, making comparisons on strings, and looking up strings to turn them back to integers.
The cool thing is that if you try to pass floats you get nice compile time errors.
I claim this to be slightly faster than other solutions as it makes less conditional evaluations which will make it behave better with CPU branch prediction.
int number = 9042100;
stringstream strm;
strm << number;
string str = strm.str();
str.erase(remove(str.begin(), str.end(), '0'), str.end());
number = atoi(str.c_str());
No string representation is used here. I can't say anything about the speed though.
int removezeroes(int candid)
{
int x, y = 0, n = 0;
// I did this to reverse the number as my next loop
// reverses the number while removing zeroes.
while (candid>0)
{
x = candid%10;
n = n *10 + x;
candid /=10;
}
candid = n;
while (candid>0)
{
x = candid%10;
if (x != 0)
y = y*10 + x;
candid /=10;
}
return y;
}
If C++11 is available, I do like this with lambda function:
int removeZeros(int candid){
std::string s=std::to_string(candid);
std::string output;
std::for_each(s.begin(), s.end(), [&](char& c){ if (c != '0') output += c;});
return std::stoi(output);
}
A fixed implementation of g24l recursive solution:
template<typename T> T nozeros(T const & z)
{
if (z == 0) return 0;
if (z % 10 == 0) return nozeros(z / 10);
else return (z % 10) + ( nozeros(z / 10) * 10);
}

Converting many bits to Base 10

I am building a class in C++ which can be used to store arbitrarily large integers. I am storing them as binary in a vector. I need to be able to print this vector in base 10 so it is easier for a human to understand. I know that I could convert it to an int and then output that int. However, my numbers will be much larger than any primitive types. How can I convert this directly to a string.
Here is my code so far. I am new to C++ so if you have any other suggestions that would be great too. I need help filling in the string toBaseTenString() function.
class BinaryInt
{
private:
bool lastDataUser = true;
vector<bool> * data;
BinaryInt(vector<bool> * pointer)
{
data = pointer;
}
public:
BinaryInt(int n)
{
data = new vector<bool>();
while(n > 0)
{
data->push_back(n % 2);
n = n >> 1;
}
}
BinaryInt(const BinaryInt & from)
{
from.lastDataUser = false;
this->data = from.data;
}
~BinaryInt()
{
if(lastDataUser)
delete data;
}
string toBinaryString();
string toBaseTenString();
static BinaryInt add(BinaryInt a, BinaryInt b);
static BinaryInt mult(BinaryInt a, BinaryInt b);
};
BinaryInt BinaryInt::add(BinaryInt a, BinaryInt b)
{
int aSize = a.data->size();
int bSize = b.data->size();
int newDataSize = max(aSize, bSize);
vector<bool> * newData = new vector<bool>(newDataSize);
bool carry = 0;
for(int i = 0; i < newDataSize; i++)
{
int sum = (i < aSize ? a.data->at(i) : 0) + (i < bSize ? b.data->at(i) : 0) + carry;
(*newData)[i] = sum % 2;
carry = sum >> 1;
}
if(carry)
newData->push_back(carry);
return BinaryInt(newData);
}
string BinaryInt::toBinaryString()
{
stringstream ss;
for(int i = data->size() - 1; i >= 0; i--)
{
ss << (*data)[i];
}
return ss.str();
}
string BinaryInt::toBaseTenString()
{
//Not sure how to do this
}
I know you said in your OP that "my numbers will be much larger than any primitive types", but just hear me out on this.
In the past, I've used std::bitset to work with binary representations of numbers and converting back and forth from various other representations. std::bitset is basically a fancy std::vector with some added functionality. You can read more about it here if it sounds interesting, but here's some small stupid example code to show you how it could work:
std::bitset<8> myByte;
myByte |= 1; // mByte = 00000001
myByte <<= 4; // mByte = 00010000
myByte |= 1; // mByte = 00010001
std::cout << myByte.to_string() << '\n'; // Outputs '00010001'
std::cout << myByte.to_ullong() << '\n'; // Outputs '17'
You can access the bitset by standard array notation as well. By the way, that second conversion I showed (to_ullong) converts to an unsigned long long, which I believe has a max value of 18,446,744,073,709,551,615. If you need larger values than that, good luck!
Just iterate (backwards) your vector<bool> and accumulate the corresponding value when the iterator is true:
int base10(const std::vector<bool> &value)
{
int result = 0;
int bit = 1;
for (vb::const_reverse_iterator b = value.rbegin(), e = value.rend(); b != e; ++b, bit <<= 1)
result += (*b ? bit : 0);
return result;
}
Live demo.
Beware! this code is only a guide, you will need to take care of int overflowing if the value is pretty big.
Hope it helps.

How to replace (or speed up) the parallel comparison when the number of pairs are extremely huge?

The run time of following code, parallel comparsion, takes forever, when the number of key in the map is huge(e.g 100000) and each of its second element have huge element(e.g 100000) as well.
Is there any possible way to speed up the the comparsion? My cpu is Xeon E5450 3.00G 4 Cores. Ram is fair enough.
// There is a map with long as its key and vector<long> as second element,
// the vector's elements are increasing sorted.
map<long, vector<long> > = aMap() ;
map<long, vector<long> >::iterator it1 = aMap.begin() ;
map<long, vector<long> >::iterator it2;
// the code need compare each key's second elements
for( ; it1 != aMap.end(); it1++ ) {
it2 = it1;
it2++;
// Parallel comparsion: THE MOST TIME CONSUMING PART
for( ; it2 != aMap.end(); it2++ ) {
unsigned long i = 0, j = 0, _union = 0, _inter = 0 ;
while( i < it1->second.size() && j < it2->second.size() ) {
if( it1->second[i] < it2->second[j] ) {
i++;
} else if( it1->second[i] > it2->second[j] ) {
j++;
} else {
i++; j++; _inter++;
}
}
_union = it1->second.size() + it2->second.size() - _inter;
if ( (double) _inter / _union > THRESH )
cout << it1->first << " might appears frequently with " << it2->first << endl;
}
}
(This is not a complete answer. It only solves part of your problem; the part about bit manipulation.)
Here's a class you might be able to use to calculate the number of intersections between two sets (the cardinality of the intersection) rather quickly.
It uses a bit vector to store the sets, which means the universe of the possible set members must be small.
#include <cassert>
#include <vector>
class BitVector
{
// IMPORTANT: U must be unsigned
// IMPORTANT: use unsigned long long in 64-bit builds.
typedef unsigned long U;
static const unsigned UBits = 8 * sizeof(U);
public:
BitVector (unsigned size)
: m_bits ((size + UBits - 1) / UBits, 0)
, m_size (size)
{
}
void set (unsigned bit)
{
assert (bit < m_size);
m_bits[bit / UBits] |= (U)1 << (bit % UBits);
}
void clear (unsigned bit)
{
assert (bit < m_size);
m_bits[bit / UBits] &= ~((U)1 << (bit % UBits));
}
unsigned countIntersect (BitVector const & that) const
{
assert (m_size == that.m_size);
unsigned ret = 0;
for (std::vector<U>::const_iterator i = m_bits.cbegin(),
j = that.m_bits.cbegin(), e = m_bits.cend(), f = that.m_bits.cend();
i != e && j != f; ++i, ++j)
{
U x = *i & *j;
// Count the number of 1 bits in x and add it to ret
// There are much better ways than this,
// e.g. using the POPCNT instruction or intrinsic
while (x != 0)
{
ret += x & 1;
x >>= 1;
}
}
return ret;
}
unsigned countUnion (BitVector const & that) const
{
assert (m_size == that.m_size);
unsigned ret = 0;
for (std::vector<U>::const_iterator i = m_bits.cbegin(),
j = that.m_bits.cbegin(), e = m_bits.cend(), f = that.m_bits.cend();
i != e && j != f; ++i, ++j)
{
U x = *i | *j;
while (x != 0)
{
ret += x & 1;
x >>= 1;
}
}
return ret;
}
private:
std::vector<U> m_bits;
unsigned m_size;
};
And here's a very small test program to see how you can use the above class. It makes two sets (each with 100K maximum elements), adds some items to them (using the set() member function) and then calculate their intersection 1 billion times. It runs in under two seconds on my machine.
#include <iostream>
using namespace std;
int main ()
{
unsigned const SetSize = 100000;
BitVector a (SetSize), b (SetSize);
for (unsigned i = 0; i < SetSize; i += 2) a.set (i);
for (unsigned i = 0; i < SetSize; i += 3) b.set (i);
unsigned x = a.countIntersect (b);
cout << x << endl;
return 0;
}
Don't forget to compile this with optimizations enabled! Otherwise it performs very badly.
POPCNT
Modern processors have an instruction to count the number of set bits in a word, called POPCNT. This is quite a lot faster than doing the naive thing written above (as a side note, there are faster ways to do it in software as well, but I didn't want to pollute the code.)
Anyways, the way to use POPCNT in C/C++ code is to use a compiler intrinsic or built-in. In MSVC, you can use __popcount() intrinsic that works on 32-bit integers. In GCC, you can use __builtin_popcountl() for 32-bit integers and __builtin_popcountll() for 64 bits. Be warned that these functions may not be available due to your compiler version, target architecture and/or compile switches.
Maybe you would like to try PPL. Or some of its analogues. I don't really understand what your code suppose to do, as it doesn't seem to have any output. But side effects free code can be effectively parallelized with tools like Parallel Patterns Library.

What's the best way to get the length of the decimal representation of an int in C++?

What's the best way to write
int NumDigits(int n);
in C++ which would return the number of digits in the decimal representation of the input. For example 11->2, 999->3, -1->2 etc etc.
Straightforward and simple, and independent of sizeof(int):
int NumDigits(int n) {
int digits = 0;
if (n <= 0) {
n = -n;
++digits;
}
while (n) {
n /= 10;
++digits;
}
return digits;
}
//Works for positive integers only
int DecimalLength(int n) {
return floor(log10f(n) + 1);
}
The fastest way is probably a binary search...
//assuming n is positive
if (n < 10000)
if (n < 100)
if (n < 10)
return 1;
else
return 2;
else
if (n < 1000)
return 3;
else
return 4;
else
//etc up to 1000000000
In this case it's about 3 comparisons regardless of input, which I suspect is much faster than a division loop or using doubles.
One way is to (may not be most efficient) convert it to a string and find the length of the string. Like:
int getDigits(int n)
{
std::ostringstream stream;
stream<<n;
return stream.str().length();
}
To extend Arteluis' answer, you could use templates to generate the comparisons:
template<int BASE, int EXP>
struct Power
{
enum {RESULT = BASE * Power<BASE, EXP - 1>::RESULT};
};
template<int BASE>
struct Power<BASE, 0>
{
enum {RESULT = 1};
};
template<int LOW = 0, int HIGH = 8>
struct NumDigits
{
enum {MID = (LOW + HIGH + 1) / 2};
inline static int calculate (int i)
{
if (i < Power<10, MID>::RESULT)
return NumDigits<LOW, MID - 1>::calculate (i);
else
return NumDigits<MID, HIGH>::calculate (i);
}
};
template<int LOW>
struct NumDigits<LOW, LOW>
{
inline static int calculate (int i)
{
return LOW + 1;
}
};
int main (int argc, char* argv[])
{
// Example call.
std::cout << NumDigits<>::calculate (1234567) << std::endl;
return 0;
}
numdigits = snprintf(NULL, 0, "%d", num);
int NumDigits(int n)
{
int digits = 0;
if (n < 0) {
++digits;
do {
++digits;
n /= 10;
} while (n < 0);
}
else {
do {
++digits;
n /= 10;
} while (n > 0);
}
return digits;
}
Edit: Corrected edge case behavior for -2^31 (etc.)
Some very over-complicated solutions have been proposed, including the accepted one.
Consider:
#include <cmath>
#include <cstdlib>
int NumDigits( int num )
{
int digits = (int)log10( (double)abs(num) ) + 1 ;
return num >= 0 ? digits : digits + 1 ;
}
Note that it works for for INT_MIN + 1 ... INT_MAX, because abs(INT_MIN) == INT_MAX + 1 == INT_MIN (due to wrap-around), which in-turn is invalid input to log10(). It is possible to add code for that one case.
Here's a simpler version of Alink's answer .
int NumDigits(int32_t n)
{
if (n < 0) {
if (n == std::numeric_limits<int32_t>::min())
return 11;
return NumDigits(-n) + 1;
}
static int32_t MaxTable[9] = { 10,100,1000,10000,100000,1000000,10000000,100000000,1000000000 };
return 1 + (std::upper_bound(MaxTable, MaxTable+9, n) - MaxTable);
}
Another implementation using STL binary search on a lookup table, which seems not bad (not too long and still faster than division methods). It also seem easy and efficient to adapt for type much bigger than int: will be faster than O(digits) methods and just needs multiplication (no division or log function for this hypothetical type). There is a requirement of a MAXVALUE, though. Unless you fill the table dynamically.
[edit: move the struct into the function]
int NumDigits9(int n) {
struct power10{
vector<int> data;
power10() {
for(int i=10; i < MAX_INT/10; i *= 10) data.push_back(i);
}
};
static const power10 p10;
return 1 + upper_bound(p10.data.begin(), p10.data.end(), n) - p10.data.begin();
}
Since the goal is to be fast, this is a improvement on andrei alexandrescu's improvement. His version was already faster than the naive way (dividing by 10 at every digit). The version below is faster at least on x86-64 and ARM for most sizes.
Benchmarks for this version vs alexandrescu's version on my PR on facebook folly.
inline uint32_t digits10(uint64_t v)
{
std::uint32_t result = 0;
for (;;)
{
result += 1
+ (std::uint32_t)(v>=10)
+ (std::uint32_t)(v>=100)
+ (std::uint32_t)(v>=1000)
+ (std::uint32_t)(v>=10000)
+ (std::uint32_t)(v>=100000);
if (v < 1000000) return result;
v /= 1000000U;
}
}
My version of loop (works with 0, negative and positive values):
int numDigits(int n)
{
int digits = n<0; //count "minus"
do { digits++; } while (n/=10);
return digits;
}
If you're using a version of C++ which include C99 maths functions (C++0x and some earlier compilers)
static const double log10_2 = 3.32192809;
int count_digits ( int n )
{
if ( n == 0 ) return 1;
if ( n < 0 ) return ilogb ( -(double)n ) / log10_2 + 2;
return ilogb ( n ) / log10_2 + 1;
}
Whether ilogb is faster than a loop will depend on the architecture, but it's useful enough for this kind of problem to have been added to the standard.
An optimization of the previous division methods. (BTW they all test if n!=0, but most of the time n>=10 seems enough and spare one division which was more expensive).
I simply use multiplication and it seems to make it much faster (almost 4x here), at least on the 1..100000000 range. I am a bit surprised by such difference, so maybe this triggered some special compiler optimization or I missed something.
The initial change was simple, but unfortunately I needed to take care of a new overflow problem. It makes it less nice, but on my test case, the 10^6 trick more than compensates the cost of the added check. Obviously it depends on input distribution and you can also tweak this 10^6 value.
PS: Of course, this kind of optimization is just for fun :)
int NumDigits(int n) {
int digits = 1;
// reduce n to avoid overflow at the s*=10 step.
// n/=10 was enough but we reuse this to optimize big numbers
if (n >= 1000000) {
n /= 1000000;
digits += 6; // because 1000000 = 10^6
}
int s = 10;
while (s <= n) {
s *= 10;
++digits;
}
return digits;
}