Count number of digits in Kotlin

This task could be solved recursively as following:

  • check it the number fits in the range -9..9. If that's true, the result is 1.
  • otherwise divide the number by 10, count digits in the result, and add 1 to that count.
fun numberOfDigits(n: Int): Int = 
    when (n) {
        in -9..9 -> 1
        else -> 1 + numberOfDigits(n / 10)
    } 

You can use the standard Java-math library in java.lang.Math (edit: since Kotlin 1.2, use kotlin.math). The log10 function will give you the length of the number minus 1 (with some exceptions). This function works with doubles though, so you have to convert back and forth.

A length function could be written like this in Kotlin:

fun Int.length() = when(this) {
    0 -> 1
    else -> log10(abs(toDouble())).toInt() + 1
}

Then you can call it like this:

println(829.length()) // Prints 3
println((-1234).length()) // Prints 4 (it disregards the minus sign)

If for some reason you don't want to resort to strings or doubles, you can use binary search on an array of integers:

private val limits = arrayOf(-1, 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999)

fun countDigits(x: Int): Int {
    assert(x >= 0)
    val result = limits.binarySearch(x)
    return result.xor(result.shr(31))   // one's complement absolute value
}

Of course, you could hard-code binary search for this specific use case if you wanted it to be really efficient, and/or you got paid by lines of code:

fun countDigits(x: Int): Int {
    assert(x >= 0)
    if (x <= 99999) {
        if (x <= 99) {
            if (x <= 9) {
                return 1
            } else {
                return 2
            }
        } else {
            if (x <= 999) {
                return 3
            } else {
                if (x <= 9999) {
                    return 4
                } else {
                    return 5
                }
            }
        }
    } else {
        if (x <= 9999999) {
            if (x <= 999999) {
                return 6
            } else {
                return 7
            }
        } else {
            if (x <= 99999999) {
                return 8
            } else {
                if (x <= 999999999) {
                    return 9
                } else {
                    return 10
                }
            }
        }
    }
}

Either way, make sure you got all those boundary cases right:

class CountDigitsTest {
    @Test fun oneDigit() {
        assertEquals(1, countDigits(0))
        assertEquals(1, countDigits(9))
    }

    @Test fun twoDigits() {
        assertEquals(2, countDigits(10))
        assertEquals(2, countDigits(99))
    }

    @Test fun threeDigits() {
        assertEquals(3, countDigits(100))
        assertEquals(3, countDigits(999))
    }

    @Test fun fourDigits() {
        assertEquals(4, countDigits(1000))
        assertEquals(4, countDigits(9999))
    }

    @Test fun fiveDigits() {
        assertEquals(5, countDigits(10000))
        assertEquals(5, countDigits(99999))
    }

    @Test fun sixDigits() {
        assertEquals(6, countDigits(100000))
        assertEquals(6, countDigits(999999))
    }

    @Test fun sevenDigits() {
        assertEquals(7, countDigits(1000000))
        assertEquals(7, countDigits(9999999))
    }

    @Test fun eightDigits() {
        assertEquals(8, countDigits(10000000))
        assertEquals(8, countDigits(99999999))
    }

    @Test fun nineDigits() {
        assertEquals(9, countDigits(100000000))
        assertEquals(9, countDigits(999999999))
    }

    @Test fun tenDigits() {
        assertEquals(10, countDigits(1000000000))
        assertEquals(10, countDigits(Int.MAX_VALUE))
    }
}

All of this assumes that you don't care about negative integers. If you do, and you have trouble adapting the code, feel free to ask for help.

Tags:

Kotlin