I want to count the number of digits before the decimal point for a numeric vector x with numbers greater or equal to 1. For example, if the vector is
x <- c(2.85, 356.01, 66.1, 210.0, 1445.11, 13.000)
my code should return a vector containing integers 1, 3, 2, 3, 4, 2
Does any know how to do this?
The formula will be integer of (log10(number) + 1). For an example, if the number is 1245, then it is above 1000, and below 10000, so the log value will be in range 3 < log10(1245) < 4. Now taking the integer, it will be 3. Then add 1 with it to get number of digits.
Now, we will see how to count the number of digits without using a loop. We can also count the number of digits with the help of the logarithmic function. The number of digits can be calculated by using log10(num)+1, where log10() is the predefined function in math. h header file.
So it can be concluded that to count number of digits, how many times a number is divided by 10 to reach 1 needs to be calculated. log base 10 of a number is the number of times a number needs to be divided by 10 to reach 1 but as 1 itself is not included in log base 10, 1 is added to get the number of digits.
This is probably the best way (for positive numbers):
floor(log10(x)) + 1
If you want an answer that works for negative numbers too, add in an abs()
:
floor(log10(abs(x))) + 1
The log10
method will not work if the input is exactly 0, so if you want a robust solution with that method, handle 0 as a special case:
n_int_digits = function(x) {
result = floor(log10(abs(x)))
result[!is.finite(result)] = 0
result
}
You can also use nchar(trunc(x))
, but this seems to behave poorly for large numbers. It will also count leading 0s, whereas the log
method will not.
For small problems, I like the nchar()
solution the best, with one modification for negative values:
nDigits <- function(x) nchar( trunc( abs(x) ) )
# Test
nDigits(100)
nDigits(-100)
# both have 3 digits
nDigits(3)
nDigits(-3)
nDigits(0.1)
nDigits(-0.1)
# all have 1 digit
nDigits(1 / .Machine$double.eps)
nDigits(-1 / .Machine$double.eps)
# both have 16 digits
If you want to make the logarithm solution work, then you need considerations for negative values and values between 0 and 1. To me, this solution is a tad more complicated:
nDigits2 <- function(x){
truncX <- floor(abs(x))
if(truncX != 0){
floor(log10(truncX)) + 1
} else {
1
}
}
Here is the output from the microbenchmark comparison (100,000 reps). The code for the character-counting solution is simpler, but slower (by a factor of 3-4):
For integers > 1 (Unit: nanoseconds):
expr min lq mean median uq max neval
nDigits(100) 1711 2139 2569.2819 2566 2994 2234046 1e+05
nDigits2(100) 0 428 861.5435 856 856 5670216 1e+05
For really tiny decimals (Unit: nanoseconds):
expr min lq mean median uq max neval
nDigits(1/.Machine$double.eps) 2994 4277 5066.321 4705 4705 4477928 1e+05
nDigits2(1/.Machine$double.eps) 428 1283 1588.382 1284 1711 2042458 1e+05
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With