Home > front end >  How to convert double to string with no zero absent in kotlin?
How to convert double to string with no zero absent in kotlin?

Time:10-29

I have two Double value but different decimal places.

123.400 // three decimal places
567.80 // two decimal places

I try to convert Double value to String by using DecimalFormat

val stringFormat = DecimalFormat("#,###.000")
println(stringFormat.format(123.400))
println(stringFormat.format(567.80))
    
val stringFormatZeroAbsent = DecimalFormat("#,###.###")
println(stringFormatZeroAbsent.format(123.400))
println(stringFormatZeroAbsent.format(567.80))

Since "0" mean Digit and "#" mean Digit with zero shows as absent, so outputs are:

123.400
567.800
123.4
567.8

The actual result i want are:

123.400
567.80

That the zero not absent.

CodePudding user response:

You could simply use String.format(String, Double) here and make the three decimal places fixed via pattern:

fun main() {
    val a: Double = 123.400
    val b: Double = 567.800

    val aStr: String = String.format("%.3f", a)
    val bStr: String = String.format("%.3f", b)

    println("${a.toString()} and ${b.toString()}")
    println("$aStr and $bStr")
}

This will output

123.4 and 567.8
123.400 and 567.800

CodePudding user response:

Like people are saying in the comments, Doubles don't actually store a specific number of trailing zeroes - 1.230000000000 and 1.23 are stored the same way. Even if you specify a certain number of zeroes, that info is lost.

If you need to store those numbers with an arbitrary number of trailing zeroes, you should probably store them as Strings - that way they'll display correctly when you print them. Just convert them to Doubles when you want to actually use them as numbers

val numberOne = "123.400"
val numberTwo = "567.80"

// just print them to get the formatting you want
println(numberOne)
// convert to Double to use it
numberOne.toDouble() * 2

Your other option is to create a class that handles this extra data. A simple one would be

data class TrailingDouble(private val asString: String) {
    val value = asString.toDouble()
    override fun toString() = asString
}

where you expose a value property instead of the caller having to call toDouble all the time.

Or you could do something a bit safer

class TrailingDouble private constructor(private val asString: String) {
    // validation done in the instance getters, which are the only thing
    // that can instantiate one of these objects
    val value = asString.toDouble()
    override fun toString() = asString
    
    // override equals etc
    
    companion object {
        fun newInstance(doubleString: String) =
            if (doubleString.toDoubleOrNull() != null) TrailingDouble(doubleString) else null
        // or if you like
        fun String.toTrailingDoubleOrNull() =
            toDoubleOrNull()?.let { TrailingDouble(this) }
    }
}

By using a private constructor, you can guarantee only valid instances of TrailingDouble can be created. That means you can't really use a data class though (you can get around private constructors with the generated copy function) so you'd have to write your own equals and hashCode functions.


And you can do the same approach with an actual Double if you like, specifying the number of trailing zeroes you want:

class TrailingDouble(val value: Double, trailingZeroes: Int) {
    private val asString: String
    
    init {
        val decimalFormat = List(trailingZeroes.coerceAtLeast(1)) { '0' }.joinToString("")
        asString = DecimalFormat("#,###.$decimalFormat").format(value)
    }
    
    override fun toString() = asString
    // equals etc
}

Basically you pass in your double (no need for validation) and the number of trailing zeroes you want, and it produces the appropriate format string to create the string representation.

This one's a lot simpler, but it does require you know how many trailing zeroes you need for a particular Double (again, that information is not contained in the Double itself, it's separate). If you're starting with Strings, the versions that work with a String are probably easier to actually use

  • Related