2

I'm creating a function that rounds large numbers over 1,000 and then returns a string of that rounded number. For example, "2374293" would return as "2.37m"
However, I dont want any extra zeros at the end of decimals like "25.00" or "100.50".

For Example:

What I want:

 Input  ->  Output

"11000" -> "11k"
"11400" -> "11.4k"

What I get:

 Input  ->  Output

"11000" -> "11.00k"
"11400" -> "11.40k"

How would I remove these zeros and decimal point(if it's a whole number) when needed?

Here is my code currently:

    private fun roundBigNumb(numb: Long): String {
    val newNumb = numb.toDouble()
    return when {
        numb in 1000..999994 -> {
            BigDecimal(newNumb/1000).setScale(2, RoundingMode.HALF_EVEN).toString()+"k"
        }
        numb in 999995..999999 -> {
            "999.99k"
        }
        numb in 1000000..999994999 -> {
            BigDecimal(newNumb/1000000).setScale(2, RoundingMode.HALF_EVEN).toString()+"m"
        }

        numb in 999995000..999999999 -> {
            "999.99m"
        }

        numb in 1000000000..999994999999 -> {
            BigDecimal(newNumb/1000000000).setScale(2, RoundingMode.HALF_EVEN).toString()+"b"
        }

        numb in 999995000000..999999999999 -> {                    
            "999.99b"
        }

        numb in 1000000000000..999994999999999 -> {
            BigDecimal(newNumb/1000000000000).setScale(2, RoundingMode.HALF_EVEN).toString()+"t"
        }

        numb in 999995000000000..999999999999999 -> {
            "999.99t"
        }

        numb >= 1000000000000000 -> "∞"

        else -> numb.toString()
    }
}
Zytron
  • 57
  • 5
  • Why do you want to show 999999999 as 999.99m instead of 1b? The rounding will be inconsistent just before a suffix change. – Tyler V Jun 12 '22 at 20:56
  • By not showing the decimal places, you're hiding the true precision of the number. But if you insist on doing this, there are [plenty of solutions](https://stackoverflow.com/questions/16984462/how-to-remove-trailing-zeros-from-a-string). – Gavin Wright Jun 12 '22 at 21:01
  • Tyler V I did that because I will use this to display both the player's balance and the cost of an item in the shop, so in the rare occasion that their balance is just under 1 billion and the item they are buying is exactly 1 billion, the player balance wont round up to 1 billion, therefore the player wont incorrectly think they can afford it, when they are actually just a couple thousand short. – Zytron Jun 12 '22 at 21:07

2 Answers2

3
import java.math.BigDecimal
import java.math.RoundingMode.HALF_EVEN

fun roundBigNumber(number: Long): String {
  fun calc(divisor: Long) = BigDecimal(number.toDouble() / divisor)
    .setScale(2, HALF_EVEN)
    .toString()
    .dropLastWhile { it == '0' }
    .dropLastWhile { it == '.' }
  return when {
    number in 1000..999994                     -> calc(1000) + "k"
    number in 999995..999999                   -> "999.99k"
    number in 1000000..999994999               -> calc(1000000) + "m"
    number in 999995000..999999999             -> "999.99m"
    number in 1000000000..999994999999         -> calc(1000000000) + "b"
    number in 999995000000..999999999999       -> "999.99b"
    number in 1000000000000..999994999999999   -> calc(1000000000000) + "t"
    number in 999995000000000..999999999999999 -> "999.99t"
    number >= 1000000000000000                 -> "∞"
    else                                       -> number.toString()
  }
}
lukas.j
  • 6,453
  • 2
  • 5
  • 24
2

Once you have converted the number to a string with up to 2 decimal places (as you are doing), you can use dropLastWhile to drop trailing zeros and decimal places.

Here is an example

fun prettyFormat(input: Double): String {
    if( input == 0.0 ) return "0"
    
    val prefix = if( input < 0 ) "-" else ""
    val num = abs(input)

    // figure out what group of suffixes we are in and scale the number
    val pow = floor(log10(num)/3).roundToInt()
    val base = num / 10.0.pow(pow * 3)

    // Using consistent rounding behavior, always rounding down since you want
    // 999999999 to show as 999.99M and not 1B
    val roundedDown = floor(base*100)/100.0

    // Convert the number to a string with up to 2 decimal places
    var baseStr = BigDecimal(roundedDown).setScale(2, RoundingMode.HALF_EVEN).toString()

    // Drop trailing zeros, then drop any trailing '.' if present
    baseStr = baseStr.dropLastWhile { it == '0' }.dropLastWhile { it == '.' }

    val suffixes = listOf("","k","M","B","T")

    return when {
        pow < suffixes.size -> "$prefix$baseStr${suffixes[pow]}"
        else -> "${prefix}infty"
    }
}

This produces

11411.0   = 11.41k
11000.0   = 11k
9.99996E8 = 999.99M
12.4      = 12.4
0.0       = 0
-11400.0  = -11.4k

If you don't care about zero or negative numbers it can be simplified a bit.

Tyler V
  • 9,694
  • 3
  • 26
  • 52
  • Thank you this was very helpful. For future reference, If I used this code again and didn't want it to round down, would I just replace "roundedDown" with "base" in "var baseStr = BigDecimal(roundedDown).setScale(2, RoundingMode.HALF_EVEN).toString()"? – Zytron Jun 12 '22 at 21:38
  • Correct, but you could end up with `1000M` then instead of `1B` for the 999.999 case. Would have to make a few other changes to address that. – Tyler V Jun 12 '22 at 21:39