Using a DecimalFormat is a perfectly good way to format a number.
However, you have a deeper issue here, which is that you should never use a Float or Double to store a value that needs to be exact, such as money.
That's because Floats and Doubles are stored as binary fractions, not decimal fractions. And just as you can't represent 1/3 exactly as a decimal fraction of any finite length (0.33333333333…), so you can't represent 1/10 exactly as a binary fraction (0.0001100110011…). So most of the decimal numbers you want to store will get approximated and rounded to the nearest binary fraction that can be stored. This isn't always obvious — when printing them out, they get rounded again to the nearest decimal fraction, and in many cases that ‘recovers’ the number you want — but there are many cases where it's noticeable, especially as the result of calculations.
You can see the effect in the Kotlin REPL:
>>> 0.1 + 0.2
res0: kotlin.Double = 0.30000000000000004
In this case, the binary fractions nearest to 0.1 and 0.2 sum to give a binary fraction that's nearer to 0.30000000000000004 than it is to 0.3.
(There are many existing questions on StackOverflow discussing this, such as here.)
So if you need your money values to be accurate (and you almost always do!), then you should store them some other way. For example, if you only ever need two decimal places (i.e. the number of paise), then simply store the number of paise as an integer. Or if you don't need to do any calculations, you could store the number as a string (which is otherwise a bad idea…).
However, the most general and flexible way in Kotlin (and Java) is to use BigDecimal. That uses decimal fractions internally to represent any decimal number exactly, to any precision you need, and you can easily do calculations and other manipulations.
In Java, using it is awkward and long-winded, but Kotlin's operator overloading makes it very natural, e.g.:
>>> val p1 = "0.1".toBigDecimal()
>>> val p2 = "0.2".toBigDecimal()
>>> p1 + p2
res3: java.math.BigDecimal = 0.3
DecimalFormat supports it too:
>>> java.text.DecimalFormat("#,##0.00").format(p1 + p2)
res4: kotlin.String! = 0.30
(Note: do not create a BigDecimal from a Float or Double, as the damage will already have been done. If you have an integer value, then start from an integer type such as Int or Long; otherwise, you'll need to start from a String to get an exact value.)
Answer from gidds on Stack OverflowFormat a double to fixed decimal length - Support - Kotlin Discussions
Print floats with certain amount of decimal numbers - Support - Kotlin Discussions
kotlin - How to format numbers with two decimal places? - Stack Overflow
android - Format number using decimal format in kotlin - Stack Overflow
This is the format you need:
val dec = DecimalFormat("#,###.##")
will print:
5.384,45
if you need always exactly 2 digits after the decimal point:
val dec = DecimalFormat("#,###.00")
The "most Kotlin-esque" way I found to do this sort of formatting is:
"%,.2f".format(Locale.GERMAN, 1234.5678) // => "1.234,57"
"%,.2f".format(Locale.ENGLISH, 1234.5678) // => "1,234.57"
"%,.2f".format(1234.5678) // => "1,234.57" for me, in en_AU
Note though that even though this is Kotlin's own extension method on String, it still only works on the JVM.
For those looking for a multiplatform implementation (as I was), mp_stools is one option.
Hello,
I'm trying to build a multiplatform library with Kotlin. The library I'm building deals with logic for formatting decimal. The only easy way I know for this would be to use Java DecimalFormat but since this is going to be multi-platform, using anything from Java is not possible. I noticed there is this open issue related to decimal formatting for Kotlin multiplatform: https://youtrack.jetbrains.com/issue/KT-21644?_ga=2.88225499.1133820560.1600725580-928101732.1566781632. So I guess currently there is no multiplatform library for decimal format? If you happened to run into this, what was your work around? Did you end up having to create your own library from scratch?
Unfortunately, there's no built-in support for formatting in string templates yet, as a workaround, you can use something like:
Use with AI"pi = ${pi.format(2)}"
the .format(n) function you'd need to define yourself as
Use with AIfun Double.format(digits: Int) = "%.${digits}f".format(this)
This will work only in Kotlin/JVM.
There's clearly a piece of functionality here that is missing from Kotlin at the moment, we'll fix it.
As a workaround, There is a Kotlin stdlib function that can be used in a nice way and fully compatible with Java's String format (it's only a wrapper around Java's String.format())
See Kotlin's documentation
Your code would be:
Use with AIval pi = 3.14159265358979323
val s = "pi = %.2f".format(pi)
Try multiplying the number by 10 until you know you got to two non zero digits. This way you will get the number of decimal points you have to round up.
After that you use the NumberFormat to format the number like this:
Use with AIval format = NumberFormat.getInstance()
format.maximumFractionDigits = nrOfDecimalPoints
val formattedNumber = format.format(yourNumber)
This way you will keep the thousands separator "," and the decimal will be cut off after two non zero digits.
EDIT
This is how the custom function looks like:
Use with AIprivate fun customFormat(number: Double): String{
var temp = number - number.toInt()
temp *= 100
var nrDec = 2
if (number <= 0.0){
return "0.00"
}
if (temp >= 1.0){
val format = NumberFormat.getInstance(Locale.US)
format.maximumFractionDigits = nrDec
return format.format(number)
}
while (temp < 1.0 && nrDec < 15){
temp *= 10
nrDec ++
}
if((temp * 10 % 10) != 0.0){
nrDec++
}
val format = NumberFormat.getInstance(Locale.US)
format.maximumFractionDigits = nrDec
return format.format(number)
}
You can do it like this in order to cut of tailing 0:
Use with AIpublic static void main(String []args){
double number1 = 0.2;
double number2 = 1.55;
double number3 = 0.00005;
double number4 = 0.50000;
DecimalFormat df = new DecimalFormat("###,##0.00#########");
System.out.println(df.format(number1)); // 0.20
System.out.println(df.format(number2)); // 1.55
System.out.println(df.format(number3)); // 0.00005
System.out.println(df.format(number4)); // 0.50
}
You just need to know how far your decimal digits should be checked.