use .ceil
math.ceil(x).toInt()
Answer from Tenobaal on Stack OverflowThe KDoc of Double.toInt() is simply inherited from Number.toInt(), and for that, the exact meaning is, it is defined in the concrete Number implementation how it is converted to Int.
In Kotlin, the Double operations follow the IEEE 754 standard, and the semantics of the Double.toInt() conversion is the same as that of casting double to int in Java, i.e. normal numbers are rounded toward zero, dropping the fractional part:
println(1.1.toInt()) // 1
println(1.7.toInt()) // 1
println(-2.3.toInt()) // -2
println(-2.9.toInt()) // -2
use this roundToInt() in kotlin
import kotlin.math.roundToInt
fun main() {
var r = 3.1416
var c:Int = r.roundToInt()
println(c)
}
Rounding usually goes to the nearest integer - 166.66 and 166.99 would both round to 170 since they're both above 166.5. That's the correct (and expected!) behaviour. But the kata tells you to round down, so truncating with toInt() should be fine - so both of those end up as 166.
The problem you're running into is a floating-point rounding error. Here's the one you're having a problem with:
assertEquals(60, dutyFree(377, 40, 9048))
which should work fine:
377 * 0.4 = 150.89048 / 150.8 = 60
but actually:
println(377.0 * 0.4)
>> 150.8
println(9048.0 / 150.8)
>> 59.99999999999999
There's a better explanation here, but basically because of the way floating-point math works, there are certain numbers you can't represent accurately, and you end up losing precision. It's similar to how 2/3 in decimal gets written as 0.6666667 - that 7 at the end is a rounding error, and so is 60 getting turned into 59.9999999
You could try using BigDecimal for more accuracy, but that's kinda complicated - what you could do instead, is add a margin of error to compensate for the rounding, by adding a very small number to the result:
fun dutyFree(normPrice: Int, discount:Int, hol:Int) : Int {
// I just pressed 0 a few times, there's no calculation behind this number, it's just small
val roundingWindow = 0.000000001
val priseWithDiscount: (Int, Int) -> Double = { a: Int, b: Int -> a*b/100.00}
val result = hol/priseWithDiscount.invoke(normPrice, discount)
return (result + roundingWindow).toInt()
}
Basically, since you're rounding down anyway, this will only affect numbers that are jussssst under the next integer, by nudging them above it so they get rounded down to that instead. If you make that adjustment too big, it'll start to push much lower numbers up to the next integer, so they're rounded up where they shouldn't be
So you have to use a small number, that's enough to round up those tiny errors (the exact number you need is called the machine epsilon but that's a complicated subject, this is just a "good enough" fix that seems fine for this situation). And when you do that, all your test cases pass!
What rule is this applying? if 166.99 -> 167 you want to round it, but if the rule you want to follow is that 166.66 -> 166 you want to truncate the double. https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.math/truncate.html