Calculating \$\lceil\log_{10}(n)\rceil\$
Especially in challenges where properties of decimal representations are of interest, one needs to find a number's decimal representation's length, or for \$n\in\mathbb{N}^+\$ equivalently, \$\lceil\log_{10}(n)\rceil\$.
In this post, several approaches are presented and discussed.
For a C source file containing approaches (a.1) to (b.3) together with a testing setup, see TIO.
I wrote this tip based on this answer of mine, searching for a smaller approach than (a.1).
If you know of any other approach possibly shorter in even a specific scenario, feel free to either add your own tip add to the list below.
(a.1) An expression in n
, 20 bytes
snprintf(0,0,"%d",n)
Versatility is a great property of the above approach, when used to determine a number's decimal representation's length, the most direct approach might be the most byte-effective.
0
is given its correct decimal representation length of 1.
GCC only throws a warning for ignoring #include <stdio.h>
, otherwise the inclusion would be very byte-heavy at potentially 19+20=39 bytes. If instead of sprintf(0
, one uses sprintf(NULL
, the byte count is increased to 23 bytes. If using both, the byte count could jump to 42 bytes.
(a.2) A function in n
, 29 bytes
a(n){n=snprintf(0,0,"%d",n);}
By being a function, this approach's versatility is high; (a.1)'s compiler-specifics also apply, when properly return
ing, the byte count increases to 5+29=34 bytes.
0
is given its correct decimal representation length of 1.
(b.1) A function in n
, requiring another variable, 32 bytes
b(n,k){for(k=0;n;k++)n/=10;n=k;}
Of all approaches presented, this one has the worst byte count. However, considering potential compiler restrictions discussed in (a.1), it could prove more byte-efficient, since the approach is nearer to the C core.
0
is given its incorrect decimal representation length of 0. One can make the case for defining \$\lceil-\infty\rceil:=0\$ which would make this approach closer to a logarithm implementation, when further defining \$\log(0):=-\infty\$.
Modification to calculate \$\lceil\log_b(n)\rceil\$ for a base \$b\in\mathbb{N}^+\$ is possible, changing the byte count by the bytes required to represent \$b\$ in C-source.
(b.2) A statement requiring existence of two variables, setting n=0
, 20 bytes
for(k=0;n;k++)n/=10;
If another variable is already declared and n
can be either discarded or needs to be cleared regardless and the use case is a whole block instead of a one-line expression, the above could be used. Since it is equivalent in byte count to (a.1), the behavioural differences have to be analyzed.
Minimally wrapped inside a function, it becomes (b.1), both being similar in features.
(b.3) A statement working additively on k
, setting n=0
, 17 (pot. 16) bytes
for(;n;k++)n/=10;
Another variation on (b.1), removing initialization of k
. Furthermore, for(;
leaves space for an expression, potentially allowing to save a semicolon's byte, effectively rendering this approach 16 bytes long.
(c.1) A statement in n
, producing stdout
-output, 14 bytes
printf("%d",n)
Courtesy of H.PWiz.
This approach's utter conciseness and simplicity may be alluring, its major drawback, however, is its stdout
-output; tolerability being heavily challenge-dependent.
(c.2) A statement in n
, producing stderr
-output, 17 bytes
dprintf(2,"%d",n)
Courtesy of Jo King.
The above is equivalent to the five bytes longer fprintf(stderr,"%d",n)
and a variant of (c.1), side-effecting not stdout
but rather the much more clutterable stderr
.
Ref. man dprintf.3