https://kotlinlang.org logo
#getting-started
Title
# getting-started
d

Danish Ansari

09/17/2021, 2:07 PM
what is the most efficient way to round
Double
up to certain decimal points, there is
round()
function in Kotlin but it always completely rounds up to 0 decimals? I want something like
round(1.4999,decimalPrecision = 2) // 1.50
j

Joffrey

09/17/2021, 2:11 PM
If your goal is just to print such a rounded value, I would go for
"%.02f".format(1.49999)
d

Danish Ansari

09/17/2021, 2:12 PM
I need it to be dynamic , I have given 2 decimal places just as an example
j

Joffrey

09/17/2021, 2:13 PM
Still, you can put a dynamic value in the format string:
Copy code
val decimalPrecision = 2
val doubleFormat = "%.0${decimalPrecision}f"
println(doubleFormat.format(1.4999))
The question is whether you need the rounded value or just a formatted representation of it
For printing you could also use Java's
DecimalFormat
(if you're on the JVM).
d

Danish Ansari

09/17/2021, 2:21 PM
As suggested, I created this function
Copy code
fun Double.round(decimalPrecision: Int): Double {
    return "%.0${decimalPrecision}f".format(this).toDouble()
}
but
65312.49999999.round(0)
is producing
65312.0
when it should give
63513.0
j

Joffrey

09/17/2021, 2:25 PM
Why do you think it should be
63513.0
? I guess it should actually be
"65312"
d

Danish Ansari

09/17/2021, 2:27 PM
1 decimal gives
65312.5
so rounding
65312.5
one more time should give
65313
, right?
j

Joffrey

09/17/2021, 2:28 PM
That is not how rounding works AFAIK, you round the actual number, not the already rounded number to another precision
But anyway, I just realized you're converting to Double again! I specifically said that this was if you wanted to format for printing not to use the rounded value as double
1
☝️ 1
If you want this kind of precision with floating point values and make calculations like that with them, I suggest you use
BigDecimal
instead
d

Danish Ansari

09/17/2021, 2:32 PM
ok sure, I'll have a look
Thanks a lot for your time
@Joffrey sorry to disturb again I modified by function like this
Copy code
fun Double.round(decimalPrecision: Int): Double {
    return BigDecimal(this.toString())
        .setScale(decimalPrecision, RoundingMode.HALF_UP)
        .toDouble()
}
but it's still giving me
1
for
1.4999
Note: I have used that BigDecimal constructor because of this SO answer and I'm infact handling currency
In general talking about Math, is the round of
1.4999
2
, or not? I'm doubting my basic education now 😂
j

Joffrey

09/17/2021, 2:55 PM
"Rounding" is imprecise terminology, but in the most common meaning of it, 1.499 is supposed to give 1 😄 there is no tie here, it's strictly less than 1.5. Now in Java, common rounding modes starting with
HALF_
are exactly about dealing with ties (e.g. when it's exactly 1.5), but it's not the case here. You could of course set the rounding mode to
UP
but in that case even 1.0001 will be rounded to 2.
Note: I have used that BigDecimal constructor because of this SO answer and I'm infact handling currency
@Danish Ansari It seems you have misunderstood the point of this answer, and my point as well. The problem is not about the constructor of
BigDecimal
itself, it's about using
double
anywhere in the code. If you use doubles, their representation in memory will fail you. So if you pass a
double
to this function, you have already lost. You need to start from
BigDecimal
right away, or from strings. Converting
this
double value to string before passing to the constructor is unnecessary because it's too late, if any error should happen with
double
it has already happened.
👍 1
1
d

Danish Ansari

09/17/2021, 3:24 PM
ok @Joffrey, got it
e

ephemient

09/17/2021, 4:27 PM
if you are rounding doubles for some reason (because as mentioned earlier, for most things like output there are better solutions) just apply scale yourself
Copy code
fun round(x: Double, decimalPrecision: Int): Double {
    val scale = 10.0.pow(decimalPrecision)
    return round(x * scale) / scale
}
👍 2
c

CLOVIS

09/20/2021, 8:32 AM
Do not use doubles or floats or anything like that for currency. Never.
☝️ 2
j

Joffrey

09/20/2021, 8:33 AM
@CLOVIS I guess the message is clearer this way than in my last message 😄
d

Danish Ansari

09/20/2021, 8:37 AM
But.. but... There is no
Cursor#getBigDecimal()
in Android SDK only
getDouble()
😅 And I am getting data from the local DB
c

CLOVIS

09/20/2021, 8:37 AM
What I've seen done is use an Int or Long to represent the amount in cents, and then divide by 100 right when displaying to the user (because Int and Long don't have rounding errors on operations)
2
Of course that only works if you're not expecting crazy high amounts, but that should be the case most of the time.
It's also very easy to work with 😅 all APIs you could think of support Int and Long, and they're multiplatform! 🙃
d

Danish Ansari

09/20/2021, 8:45 AM
That would require a huge huge rework for the system 😅
c

CLOVIS

09/20/2021, 10:07 AM
I don't want to be rude, but that's necessary. If you do anything even half serious with currency, you're going to have huge issues if you keep floats. Please read https://en.m.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems
👌 1
For example, your current system cannot deal with $0.10 (or whatever your currency is).
Although the given cases for π etc are probably not a concern for you, the examples will addition will definitely be.
e

ephemient

09/20/2021, 4:39 PM
feel free to make your own value class representing fixes point decimals, or BigDecimal if you don't mind the hit. but 100% do not use floating point. https://husobee.github.io/money/float/2016/09/23/never-use-floats-for-currency.html
309 Views