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, Double
s 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 String
s - that way they'll display correctly when you print them. Just convert them to Double
s 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 String
s, the versions that work with a String
are probably easier to actually use