number of digits in a number for any given base

 

Overview

In software Engineering and Computer Science or Mathematics, you often want to know how many digits or bits there are in a number so you can allocate memory space for the character strings. The equation for this for integers is simple.

My instructor Taylor Hannah at Portland Community College gave us a start in the right direction or he gave us the whole thing (memory is fuzzy after that many years) and told us how the equation worked, for which I am grateful. I have been trying to take what I remembered and make it work as flawlessly as I can. if I remember right, he gave us log(n)/log(base) and mentioned that any fractional part if it exists should be converted to the next whole integer up (this is not round() but ceil()). maybe it was ln(n+1)/ln(base). He may have given us a caveat with this (I don't remember entirely, it's been 20+ years). I decided I wanted to make it work with negative numbers. log doesn't do that. so you have to abs(n).

I tried some functions here on this page. previous stuff was incorrect and I said it was experimental. former stuff on this page (except for the integer-only function) gave incorrect results. I got better results, (but not perfect) with numdigits=ceil(log number/log base)+1-((log number/log base)-floor(log number/log base)). but this wasn't it either.

see Solutions for the working results.

quick-reference for commonly used integer sizes

  • 8 bits, number of digits=ceil(abs(log(2^8-1;10)+1)/log(10;10))=

integer number of digits in a number calculators

Instructions: you can use math, or you can use an integer followed by an optional suffix which consists of a : followed by an IEC or SI unit as a suffix. Example: 1,000:Mi 22:G 64

calculate number of digits in n



Result:

calculate number of digits in n bits



Result:

Solution 1: fast, but approximate results for non-zero integers (due to floating point)

I believe I have finished experimenting to find out what is the correct function for this. I believe I have arrived at both a math and an iterative solution with the math solution being ceil(log(abs(number) + 1) / log(base)) where number is not 0 (in that case, the result should always be a 1 so you really need an if statement in there, which math and higher calculators provide). however, this works only where number is >=0. it would require another if to handle putting the - sign back in (because number has been abs()'d).

the reason why number is abs()'d is that if number goes negative, log treats the value as if it were a fraction. this is why I think there is a possibility somehow for handling the fractional part of a number (separately of course). I would use the function frac() and int(). the biggest problem is, how to get a result that is an integer and not a fraction or something that's just way too large to be usable. I am unsure how to go about this.

ceil() is a function that, if a number is any fraction over 0, converts it to the next whole integer (but it outputs a double/real version of an integer in C/C++ as with most languages). the ceil() function only works in one direction I believe. UP. if you are working with negative numbers and you want to take the next whole integer DOWN, you have to use floor(). so if you are going to make a function which handles negative numbers in this fashion, I suggest you use an if statement. but I don't need to here since I am using abs().

the real number domain of the log(n) function is n from 1 and above and anything else is an error (well, really it's a complex number if I remember right, but you really don't want to mess with this when you are dealing with reals).

regardless of whether or not the number is negative, the number of digits should always be a positive result, so we abs() or fabs() the number (depending on which language you are using).

about raising to a power, digit positions, and number base

nearly every programmer knows 2^9=512 and 2^10=1024, right? not so fast.

you can have those powers memorized, yes, but know that those powers are digit positions, not numbers of digits. this is critical when doing number to string and string to number conversions, because for each digit you use Σ digitvalue*base^zerobaseddigitposition.

so what is 210^910 in binary? 1010 digits worth of 10000000002, however, that 210^910 can be used to represent 010..51110 which is 010..210^910-110, since 51110=1111111112 and this is very useful to know for programming, since if we have 9 bits (such as a number of bits per disk sector or soemthing) it must start with 0 as a beginning value and because it is 0-based, its upper limit is 210^910-110=51210-110=51110.

test cases

case #1: 210^910=51210=10000000002 (1010 base-210 binary digits) 910 02's led by a 12). for 210^910, for example, the 910 is the 010-based digit position as in 010..910, not the number of digits. 1010 is the number of base-210 (binary) digits.

case#2: 210^1010=102410=100000000002 (1010 02's led by a 12). for 210^1010, for example, the 1010 is the 010-based digit position as in 010..1010, not the number of base-210 (binary) digits.

case#3: 210^910-110=51110=1111111112 (910 base-210 binary digits)

numdigits in ttcalc: ceil(log((abs(n)+1);10)/log(base;10))
OR
numdigits = ceil log  abs number + 1 log  base

equation section

equation properties

  • works for positive integers only. the border case for negative numbers and 0 must be handled using if then else endif statements or the ternary operator. for negative, you would have to create a different function for string length since the - sign is not a digit and would not be counted. 0 is not currently counted and returns 0.
  • you can use any base log function you like, like ln (loge) or log10, as long as you use the same thing in all instances of the equation.

equation test cases

border test case #1: n=-110,base=210 renders 1010. success, however, note that the - sign is not a digit and is therefore not counted. this is not a string-length function, so to make a string-length function, you would add 1 for the string length of the decimal point (period, comma in some countries) in the case where integer portion of mantissa or the integer is negative.

border test case #1: n=010,base=210 renders 010. failure.

border test case #1: n=110,base=210 renders 110. success.

border test case #2: n=51210,base=210 renders 1010. success.

border test case #3: n=51110,base=210 renders 910. success.

border test case #4: n=511.210,base=210 renders 1010. failure. should be 1110 digits total, not 1010. however, does not count the decimal point which would make it 12, but does count the extra digit (I guess). I suppose you have to decide between string length and number of digits because these are not the same thing when it comes to real numbers (floating point). so failure for floating point or real numbers on first try, so that means this is an integer-only function.

border test case #3: n=511.2210,base=210 renders 1010. failure. does not cound the decimal point, but does count the extra digit (I guess). I suppose you have to decide between string length and number of digits because these are not the same thing when it comes to real numbers (floating point). so failure for floating point or real numbers.

equation

numdigits in ttcalc: ceil(log((abs(n)+1);10)/log(base;10))
OR
numdigits = ceil log  abs number + 1 log  base ,

if (0==number) {
    return 1;
}
if (number<0) {
    number++;
}

I have tested many equations. the above function is good for all positive numbers except zero (0). If you want very accurate results, use integers instead with repetition.

#if defined(__cplusplus)
//c++ has bool, c99 only has bool in stdbool.h
#include <cstdint>
#include <cmath> //you can use cmath if you are using c++. this provides log and ceil
#else
#include <stdbool.h>
#include <stdint.h>
#include <math.h> //you can use cmath if you are using c++. this provides log and ceil
#endif

/* this function works with integers 0 and greater. it tells you the number of digits required. */
int64_t NumberOfDigits(int64_t number, int64_t base, bool includeSignAsDigit) {
    //int64_t signDigits;
    //if (0 == number) {
    //    return 1;
    //}
    //signDigits=((includeSignAsDigit&&(number<0))?1:0);
    //return int64_t(LD(ceil(LD(log(LD(abs(LD(number)))+LD(1)))/LD(log(LD(base))))+signDigits));
    //if you have it, this sort of eliminates the if statement. it's still there, it's just a ternary operator,
    //but it can be inlined.
    //return int64_t(LD(ceil(LD(log(LD(abs(LD((0==number)?1:number)))+LD(1)))/LD(log(LD(base)))))) + signDigits;
    //sticking it all into one line:
    #if defined(__cplusplus)
    return int64_t(LD(ceil(LD(log(LD(abs(LD((0==number)?1:number)))+LD(1)))/LD(log(LD(base)))))) + ((includeSignAsDigit&&(number<0))?1:0);
    #else
    return int64_t(ceill(logl(fabsl((0==number)?1:number)+LD(1))/logl(base))) + ((includeSignAsDigit&&(number<0))?1:0);
    #endif
	/*
	ceil() is a function that, if a number is any fraction over 0, converts
	it to the next whole integer (but it outputs a double/real version of an
	integer in C/C++ as with most languages). the ceil() function only works in
	one direction I believe. UP. if you are working with negative numbers and you
	want to take the next whole integer DOWN, you have to use floor(). so if you
	are going to make a function which handles negative numbers in this fashion,
	I suggest you use an if statement. but I don't need to here since I am using abs().
	
	the real number domain of the log(n) function is n from 1 and above and
	anything else is an error (well, really it's a complex number if I remember
	right, but you really don't want to mess with this when you are dealing with reals).
	
	regardless of whether or not the number is negative,
	the number of digits should always be a positive result,
	so we abs() or fabs() the number (depending on which language you are using).
	
	*/
	
	/*double lb=log(number)/log(base);*/ /*old method commented out, didn't work with 000's*/
    /*return ceil(lb)+1-(lb-floor(lb));*/ /*old method commented out, didn't work with 000's*/
    /*
    lb-floor(lb) represents the fractional
        part (everything after the decimal point).
    1-the above is basically a reverse of
        the range of the fractional part.
    the above is added to ceil(lb), which has
        proved to be closer to the actual
        result in most cases.
    the whole result is converted to an
        integer (equivalent of floor().
    */
}

Future

  • calculate number of digits in a number for real (floating point) numbers.
    I have a thought of:
    • inverting the value (1/x)
    • finding a way to shift the digits right of the decimal point to the left of the decimal point (like a string, effectively zapping the decimal point and everything left of it.
    • coming up with a function that simply calculates the number of digits in the fractional portion of a real number. I think this is do-able. there must be some way of reversing the division of log functions to make that work for fractional stuff, but it's not as simply as using 10^n instead of log(n;10).
    • I intend to sum up the 2 resulting functions, the integer digits count portion, and the frac digits count portion.
    • there could be an additional function made which returns the string length by adding 1 to the frac count if frac count is not 0.
    this was not done by my professor. I have so far the failed: fracdigitscount=ceil(log( 1+int(abs(frac(511.))) ;10)/log(10;10)) and many similar failed guesses that don't fit regular test cases or border conditions. I know log(n/10) is the reverse of 10^n.
    but ttcalc does not have a function for these. being able to calculate it with an HP-50g calculator would be fine.

    test cases:
    • n=51110,base=1010
    • n=511.10,base=10
    • n=511.010,base=1010
    • n=511.110,base=1010
    • n=511.910,base=1010
    • n=511.0010,base=1010
    • n=511.0110,base=1010
    • n=511.1110,base=1010
    • n=511.9910,base=1010
    • n=511.00010,base=1010
    • n=511.00110,base=1010
    • n=511.11110,base=1010
    • n=511.99910,base=1010
    the frac digits function should ignore the integer portion of the mantissa.
  • coming up with a way in ttcalc to handle inline if statements to handle the 0 case which returns 0 for integers but should return 1. the HP-50g calculator and all computers have this capability in programming languages either through the if (condition) then statement else statement endif statement or the ternary operator ((condition)?trueValue:falseValue)

Conversion Examples

these are calculated using ttcalc.

binary (base 2) 10101000 (decimal 168). on ttcalc, ceil(log(abs(&10101000)+1;10)/log(2;10))=8. indeed, there are 8 digits.

octal (base 8) 76543210 (decimal 16434824). on ttcalc, ceil(log(abs(16434824)+1;10)/log(8;10))=8. indeed, there are 8 digits.

decimal (base 10) 400 (decimal 400). on a calculator, ceil(log(abs(400)+1;10)/log(10;10))=3. indeed, there are 3 digits.

hexadecimal (base 16) FEED (decimal 65261). on a calculator, ceil(log(abs(#feed)+1;10)/log(16;10))=4. indeed, there are 4 digits.

Solution 2: relatively fast, iterative results, integer only

a sure-fire method is below:

#if defined(__cplusplus)
#include <sstream>
#include <string>
#endif
#include <stdint.h>

/*this should work for integers. it returns the number of digits required*/
int64_t NumberOfDigits(int64_t number, int64_t base) {
    int64_t divided, numdigits = 0, numberBackup=number;
    /*
    handle border case of 0, in which 0 counts as a digit,
    normally we ignore zeros, but always in math we show
    the number 0 instead of deleting it as nonexistent.
    this is the funny rule in math they don't really explain too well.
    */
    if (0 == number) {
        return 1;
    }
    //count digits
    while (0 != number) {
        number /= base;
        /*
        takes advantage of integerized divides
        which truncate the fractional portion off
        since this is an integer type.
        a floating point or decimal would not do this.
        */
        numdigits++;
    }
    return numdigits;
}

//this should work with negative numbers.
int64_t numdigits(int64_t number, bool includeAnyMinusSign) {
	   #if defined(__cplusplus)
    std::ostringstream oss;
    oss<<number;
    if (!includeAnyMinusSign &&
        oss.str().size()>=2 &&
        '-'==oss.str()[0]) {
    	return oss.str().substr(1).size(); //exclude - sign from count
    } else {
        return oss.str().size();
    }
    #else
    char numstr[100];
    #if defined(__MINGW32__)||defined(__BORLANDC__)||defined(_MSC_VER)
    int64_t n=sprintf(numstr, "%I64d", number);
    #if defined(__DJGPP__)
    int64_t n=sprintf(numstr, "%lld", number);
    #endif
    if (n < 0) {
        return n-1; //account for sign and don't include it
    } else {
        return n;
    }
    #endif	
}

int64_t numdigitsd(long double number, int64_t base, bool includeAnyMinusSign) {/*0==failure*/
    #if defined(__cplusplus)
    std::ostringstream oss;
    oss.setf(std::ios_base::fixed)
    oss<<number;
    std::string s=oss.str(),expchars="eEpP",signchars="+-";
    size_t digitCount=s.size(),pos=0,prev_pos=pos;
    if (std::string::npos!=s.find_first_of(expchars)) {
        /*
        this is not such a simple job,
        can't really do this if it's in
        in scientific format since we can't count the
        exponent digits.
        unless, however, we force the stream into
        std::ios_base::fixed, but not sure how this is
        going to work for irrational numbers.
        */
        return 0;/*failure*/
    }
    /*
    pos already 0. at this point, doesn't have an exponent,
    just mantissa.
    find every sign char and subtract its count from the string length, if

    */
    if (!includeAnyMinusSign) {
        do {
            pos=find_first_of(signchars,prev_pos);
            if (std::string::npos!=pos) {/*found sign?*/
                digitCount--;/*uncount it*/
            }
        } while (std::string::npos!=pos);
    }
    return digitCount;
    #else
    /*
    number==0 border case, mathematics special unsaid rule
    that 0 itself is always shown, whereas leading digits
    before integer portion and trailing digits after
    fractional portion are not displayed or shown or counted.
    */
    if (0==number) {
        return 1;
    }

    int64_t mantissaIntDigitCount=ceil(logl(abs(number)+1)/logl(base));
    int64_t mantissaFracDigitCount=SOMEFUNCTIONHERE; /*!!!!!fill in with frac digit counting function*/
    int64_t signcount=(includeAnyMinusSign && number < 0?1:0); /*count border case of leading minus sign*/
    return intdigits + fracdigits + signcount;
    #endif	
}

while n>0, while n!=0, , , , , , , , , , , , , ,

Fixed Point Idea

the good results I have here are only for integers. they are not intended for fixed point or scientific format numbers.

well, the integer part of the number is done... I do have some ideas about fixed point's fractional part though. fixed point would be a little easier to calculate since it would be nearly a constant. well, you have to trim off the trailing zeros in the reverse direction, so I guess it would be a case of simply doing a 1/number on the math version of this algorithm possibly, since the algorithm does the exact same thing for the same set of bits the only thing you have to do is flip the bits 180°, make the fractional part the integer part and the integer part the fractional part (the front to the back and the back to the front) and treat those fractional parts which are now in the integer position like an integer and process them.

this won't work as perfectly if the number of bits isn't equal on both sides of the virtual decimal point. I thought you could stuff it into a c++ dynamic_bitset type and use algorithm to reverse() it and convert it back to an int64_t with to_ullong().

ttcalc and ttmath.org and gnu GMP (visit your favorite gnu gcc compiler collection like mingw-w64) has bignum libraries for dealing with fixed point math. google them if you need to.

floating point idea

I have SOME ideas about floating point - floating point doesn't have really well-defined results.

to work with floating point you are probably going to have to manually dig into the latest version of the IEEE-754 format specification and interpret what those bits are doing. there is a mantissa, sign, and exponent which also has a sign.

and by the way, there is +0, -0 I think and they are not the same value. possibly that was one of the issues addressed in the new IEEE-754 format and also in the latest Numeric Processor microcode.

the main problem I have with floating point is that if you have a really big number, you don't have enough bits to represent all the lower end bits, so floating point makes up for it by chopping off those bits and leaving only the upper n bits. that's the mantissa. always you have n bits of real mantissa number. representing the msb (most significant bits) part of a number has of course higher priority than the lsb (least significant bit(s)), which get lopped off in the conversion or math processes.

I know one solution is I think a series sum: the

numIntegerDigits = ceil(log(1+abs(n))/log(base))

                fracLastDigitPos
numFracDigits = Σ(fracDigit*10^(pos-fracLastDigitPos))
                pos=-1

where fracLastDigitPos < 0

to get numdigits, simply add the two together.
to account for decimal point, add 1.
to account for sign, add 1 more if < 0.
, pos representing the digit positions of the frac part of the number (frac() digit positions are always negative while int() digit positions are always positive). this effectively appears to shift the bits, but in floating point it actually swivels them. but this is not going to work well UNLESS some sort of cpu instruction provides this functionality. Intel, hint hint! I suggest creating the instruction frev.

numIntegerDigits = ceil log 1 + abs n log base

numFracDigits = ceil log 1 + abs Σ pos = -1 lastFracDigitPos fracDigit × 10 pos - lastFracDigitPos log base

numDigits = numIntegerDigits + numFracDigits

so, I suppose that your task, would be to follow the following algorithm:

/* js - this function works with integers, positive, negative, and 0, and tells you the number of digits required. */
function NumberOfDigits(n, base, countMinusSign) {
    //type checking
    if ('number'!=typeof(number)) {
        alert("NumberOfDigits() was fed type "+typeof(n)+" for n, should be Number");
        return 0;//failure
    }
    if ('number'!=typeof(base)) {
        alert("NumberOfDigits() was fed type "+typeof(base)+" for base, should be Number");
        return 0;//failure
    }
    if ('boolean'!=typeof(countMinusSign)) {
        alert("NumberOfDigits() was fed type "+typeof(countMinusSign)+" for countMinusSign, should be Boolean");
        return 0;//failure
    }
    //zero special case
    if (0 == number) {
        return 1;
    }
    //equation
    return Math.ceil(Math.log(Math.abs(number) + 1) / Math.log(base)) +
           ((countMinusSign && (number < 0))?1:0);
}
#include <stdint.h>
#include <math.h> //you can use cmath if you are using c++. this provides log and ceil

/* this function works with integers 0 and greater. it tells you the number of digits required. */
int64_t NumberOfDigits(int64_t number, int64_t base, bool countMinusSign) {
    if (0 == number) {
        return 1;
    }
    return int64_t(ceil(log(fabs(number) + 1) / log(base))) +
           ((countMinusSign && (number < 0))?1:0);
	/*
	ceil() is a function that, if a number is any fraction over 0, converts
	it to the next whole integer (but it outputs a double/real version of an
	integer in C/C++ as with most languages). the ceil() function only works in
	one direction I believe. UP. if you are working with negative numbers and you
	want to take the next whole integer DOWN, you have to use floor(). so if you
	are going to make a function which handles negative numbers in this fashion,
	I suggest you use an if statement. but I don't need to here since I am using abs().
	
	the real number domain of the log(n) function is n from 1 and above and
	anything else is an error (well, really it's a complex number if I remember
	right, but you really don't want to mess with this when you are dealing with reals).
	
	regardless of whether or not the number is negative,
	the number of digits should always be a positive result,
	so we abs() or fabs() the number (depending on which language you are using).
	
	*/
	
	/*double lb=log(number)/log(base);*/ /*old method commented out, didn't work with 000's*/
    /*return ceil(lb)+1-(lb-floor(lb));*/ /*old method commented out, didn't work with 000's*/
    /*
    lb-floor(lb) represents the fractional
        part (everything after the decimal point).
    1-the above is basically a reverse of
        the range of the fractional part.
    the above is added to ceil(lb), which has
        proved to be closer to the actual
        result in most cases.
    the whole result is converted to an
        integer (equivalent of floor().
    */
}

enum {FR_SCIENTIFIC,FR_FIXED};

int64_t NumberOfDigitsD(double number, int64_t base, int format_requested) {
    switch (format_requested) {
    case FR_SCIENTIFIC: {
        display as scientific format
    }
    break;
    case FR_FIXED: {
        int64_t intportion=int64_t(number), fracportion = int64_t(1.0/number), numdigitsint=NumberOfDigits(intportion,base), numdigitsfrac=NumberOfDigits(fracportion,base);
        if (0==fracportion) {
            return numdigitsint; //show as plain integer (well, maybe you need to poitn in some e3xtra code if someone wants special formatting like zero padding or something)
        } else {
            return numdigitsint + 1 + numdigitsfrac; //show as fixed point. account for the decimal point character
        }
    }
    break;
    default: {
        int64_t md = number of mantissa digits;
        if (0 == exponent) {
            convert mantissa and sign to 64-bit integer
            find number of digits from integer result of above operation
            display as integer
        } else if (abs(exponent) < md) { //I could be off by 1
            determine where decimal point is by interpreting exponent.
            int64_t intportion=int64_t(number), fracportion = int64_t(1.0/number), numdigitsint=NumberOfDigits(intportion,base), numdigitsfrac=NumberOfDigits(fracportion,base);
            int64_t intportionsign = ((intportion<0)?1:0);
            if (0==fracportion) {
                return intportionsign + numdigitsint; //show as plain integer
                //(well, maybe you need to point in some e3xtra code if someone wants special formatting like zero padding or something)
            } else {
                return intportionsign + numdigitsint + 1 + numdigitsfrac; //show as fixed point. account for the decimal point character
            }
        } else {
            //although here if you wanted to, you could just look at the scientific format's digits instead of the virtual number.
            //such as exponent, mantissa, and count both sets of signs if they are negative.
            if (number < 1) {
                return NumberOfDigits(1/number,base);
            } else {
                return NumberOfDigits(number,base);
            }
        }
    }
    break;
}

yeah, you might have to write an entire output routine to do this sort of thing if you want to go that far...

notes:

for +int: numdigits=ceil(log((abs(n)+1);10)/log(base;10))
for base 10 +int: numdigits=ceil(log((abs(n)+1);10))

chicken and egg problem:
if I knew the count of the digits AFTER the decimal point,
I could simply multiply the fractional part
by 10^numdigits to move it to left of decimal point.

I know that .1111 I am using is base 10 number system,
and because of this, I should use base 10 log(n;10) and base 10 antilogs (10^n).
but what configuration?

log(.1111)=-0.95428594105
10^.1111=1.29151662207
.1111^10=0.2865105309e-9
((( abs(frac(511.11111111111111)) )^10)*10^10)=2.867
10^( abs(frac(511.11111111111111)) )=1.291,549,665
ceil(10*10^( abs(frac(511.111)) )+1)=14
log( abs(frac(511.11111111111111)) ;10)=-0.954,242,509,439,329
log( abs(frac(511.1111)) ;10)          =-0.954,285,941,059,132
10^2/10^.1111=77.428,349,191

41 digits below according to notepad++:
as lim(frac(n))→1, log(frac(n);10)→0
as lim(frac(n))→1, 10^frac(n)→10
as lim(frac(n))→0 {n≠0, log(frac(n);10)→1
as lim(frac(n))→0, 10^frac(n)→1
(log(.99999999999999999999999999999999999999999;10))=-0.000,000,000,000,000,000,000,000,000,000,000,000,000,004,342,944,819
log(.1111111111111111111111111111111111111111;10)=-0.954,242,509,439,324,874,590,055,806
10^(.1111111111111111111111111111111111111111)=1.291,549,665,014,883,875,410,075,546,472
log(10;10)/log(.1111111111111111111111111111111111111111;10)=-1.047
ceil(abs(1/log(abs(log(.1111111111111111111111111111111111111111;10));10)))=50
(10^.1111111111111111111111111111111111111111)^10=12.915
abs(log(10;10)/log(abs(.1111111111111111111111111111111111111111)+1;10))=21.854
log(10;10)/log( 10^.111111111111/abs(log( abs(.111111111111) ;10)) ;10)=7.607

.111111111 9 digits
log((abs(.111111111)*abs(log(abs(.111111111);10)));10)=-0.974
log(n*abs(log n))=log n
10^abs(log(.1111111111111111111111111111111111111111;10))=9.000,000,000,000,000,000,000,000,000,000,00
10^abs(log(.111111111;10))=9.000,000,009,000,000,009