K.Os
K.Os

Reputation: 5506

Kotlin how to format Double number to String without zero after comma

Suppose I have Double number which I want to convert to String. I want to have an option which based on that I would have String number without trailing zeroes.

So for example:

Is there a nice way to do this in Kotlin?

This is code which I have which I feel is rather ugly:

private const val VALUE_DIVIDER = 100
private const val DIGITS_AMOUNT = 2
private val defaultLocale = Locale("us")
private val currency = "$"
private val cents = 10000

    fun print(withoutTrailingZeros: Boolean = true): String {
        val price = (cents.toDouble() / VALUE_DIVIDER)
            .valueToString()
            .let { if (withoutTrailingZeros) it.removeSuffix(",00") else it }
        return "$price $currency"
    }

    private fun Double.valueToString() = round(DIGITS_AMOUNT).replace(".", ",")

    private fun Double.round(digits: Int): String =
        NumberFormat.getInstance(defaultLocale).apply {
            maximumFractionDigits = digits
            minimumFractionDigits = digits
            isGroupingUsed = false
        }.format(this)

UPDATE: The solution provided by @Roma Pochanin works partially, but strangely only as jUnit tests. After I am running integration tests on Android emulator using this logic this is not working for 0 (it is formatted as "0,00" even when the withoutTrailingZeros flag is true). I heard about some bug related to that Why does new BigDecimal("0.0").stripTrailingZeros() have a scale of 1? but how it is connected with my case? Can anyone explain?

Please, see the exact sessions from debugger:

Upvotes: 3

Views: 3535

Answers (3)

Yvgen
Yvgen

Reputation: 2212

.toBigDecimal().stripTrailingZeros()

Upvotes: 0

Willi Mentzel
Willi Mentzel

Reputation: 29924

There is no function for that in the Kotlin standard library, but you can specify the number of decimal places and the decimal format symbol using Java's DecimalFormat:

val formatSymbols = DecimalFormatSymbols.getInstance().apply {
    decimalSeparator = ','
}

val twoDecimalDigitsFormat = DecimalFormat("#.##").apply {
    decimalFormatSymbols = formatSymbols
}

val twoTrailingZerosFormat = DecimalFormat("#.00").apply {
    decimalFormatSymbols = formatSymbols
}

fun formatPrice(price: Double, withDecimalZeros: Boolean) = if (withDecimalZeros) {
    twoTrailingZerosFormat
} else {
    // Is number still the same after discarding places?
    if (price.toInt().toDouble() == price) {
        twoDecimalDigitsFormat
    } else {
        twoTrailingZerosFormat
    }
}.format(price)


println(formatPrice(123.00, true)) // 123,00
println(formatPrice(123.324, true)) // 132,32

println(formatPrice(123.00, false)) // 123
println(formatPrice(123.324, false)) // 123,32

Upvotes: 2

Roma  Pochanin
Roma Pochanin

Reputation: 302

Why don't you use BigDecimal? It's like the default way to deal with prices and similar stuff. You also can consider using BigDecimal's method stripTrailingZeros:

private const val VALUE_DIVIDER = 100
private const val DIGITS_AMOUNT = 2

private val currency = "$"
private val cents = 1298379

fun formatPrice(withoutDecimalZeros: Boolean = true) =
        BigDecimal(cents)
                .divide(BigDecimal(VALUE_DIVIDER), DIGITS_AMOUNT, RoundingMode.UP)
                .let { if (withoutDecimalZeros) it.stripTrailingZeros() else it }
                .toString().replace(".", ",")
                .let { "$it $currency" }

Upvotes: 1

Related Questions