Reputation: 5506
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:
option with trailing zeros 123.00 -> "123,00", 123.324 -> "123,32"
option without trailing zeroes 123.00 -> "123", 123.324 -> "123,32"
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:
working, as jUnit tests: https://ibb.co/HN9n41T
bug, when running instrumentation tests on Android emulator: https://ibb.co/VCrmrMh
Upvotes: 3
Views: 3535
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
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