Allows safe and developer friendly arithmetic in idiomatic Kotlin, built on top of BigDecimal. Prevents accidental precision loss during arithmetic operations by enforcing type safety on limited precision arithmetic.
Kotlin
Add the following to your `build.gradle.kts`:
implementation("com.konigsoftware:konig-decimal:1.0.0")
Groovy
Add the following to your `build.gradle`:
implementation 'com.konigsoftware:konig-decimal:1.0.0'
The KonigDecimal
class is the main class that represents arbitrary precision decimal values. It supports
various constructors for creating instances from different types such as String, Int, and Double:
val value1 = KonigDecimal("10.123")
val value2 = KonigDecimal(5)
val value3 = KonigDecimal(1.24325)
val result = value1 + value2 + value3
println(result) // Output: 16.36625
There is also a convenience method for constructing a KonigDecimal
from a Long
given a certain LongUnit
:
val value1 = KonigDecimal.fromLong(10145L, LongCentis) // equivalent to 101.45
val value2 = KonigDecimal("1.23")
val result = value1 + value2
println(result) // Output: 102.68
This method can be helpful when interacting with applications that represent floating point numbers as integers with some scale attached. A common example is an application that represents US Dollar amounts in cents rather than the actual dollar amount (ie: 10145 = $101.45).
This library comes with several pre-existing LongUnit
's, but you can easily add your own units when needed. See here for
an example custom LongUnit
.
An arbitrary precision KonigDecimal
can be rounded to a fixed precision FixedKonigDecimal
with a given scale. The scale
is represented by the KonigDecimalScale
class
which comes with several predefined scales like Nanos, Micros, and Centis. Again you can easily add your own custom scale
by following the example here.
val arbitraryPrecision = KonigDecimal("1.012345678909876543690")
val roundedCentis = arbitraryPrecision.roundToScale(Centis) // Result: FixedKonigDecimal<Centis>("1.01")
val roundedNanos = arbitraryPrecision.roundToScale(Nanos) // Result: FixedKonigDecimal<Nanos>("1.012345679")
val roundedMicros = arbitraryPrecision.roundToScale(Micros) // Result: FixedKonigDecimal<Micros>("1.012346")
Arithmetic operations between two FixedKonigDecimal
's with different scales will not be allowed by the type system. This
prevents accidental operations between two fixed precision numbers with different scales that could result in a loss of precision.
The developer must explicitly round two numbers to the same fixed precision before they can perform arithmetic on the numbers.
val value1 = KonigDecimal("1.012492414").roundToScale(Centis)
val value2 = KonigDecimal("39.29490358234").roundToScale(Micros)
val result = value1 * value2 // <-- !Results in compiler error!
A FixedKonigDecimal
can be converted into a Long
using the toLong
method. Again, this can be helpful when interacting with applications that represent floating point numbers as integers with
some scale attached.
val arbitraryPrecisionAmount = KonigDecimal("12.981240")
val amountCents = arbitraryPrecisionAmount.roundToScale(Centis).toLong(LongCentis)
println(amountCents) // Output: 1298
// You can optionally use a convenience method to produce the same result
val amountCents2 = arbitraryPrecisionAmount.roundToCentisAsLongCentis()
println(amountCents) // Output: 1298
To add a custom LongUnit
simply
implement the LongUnit
interface. See example below:
// 1 CustomUnit = 0.0000001
object CustomUnit : LongUnit {
override val oneInLongUnit = KonigDecimal(10_000_000)
}
To add a custom KonigDecimalScale
simply implement the KonigDecimalScale
interface. See example below:
// MyCustomScale has a scale of 7
// Scale is defined as the total number of digits to the right of the decimal point
object MyCustomScale : KonigDecimalScale {
override val scale = 7
override val roundingMode = RoundingMode.HALF_EVEN
}