I’ve noticed that Rust has the d128 primitive type (128 bit exact decimal fractional type). Is adding the d128 basic type in the plans for a future release of Kotlin? Fixed decimal arithmetic is critical for many business-oriented applications. (Did you see the movie “Office Space”?)
Why not use Java
BigDecimal? There could be minor memory usage and performance deficiencies compared to fixed size primitives, but on other hand, they are much more powerful and have tight integration with the ecosystem.
Yes, BigDecimal is functionally sufficient, but BigDecimal isn’t a basic type, so I can’t do this:
var y: d128 = 2d
var z: d128 = 3d
var x = y + z
and if type inference for the basic type “decimal” worked then it could be shortened to
var y = 2d
var z = 3d
var x = y + z
To use BigDecimal, I must instead do this:
var y: BigDecimal(2)
var z: BigDecimal(3)
var x: BigDecimal() = y.add(z)
The verbosity of the declaration is inconvenient, but the verbosity of complex expressions is enough to make Kotlin very unattractive for business applications. Just imagine what a calculation like x = (a * .95) + b / 12 would look like with BigDecimal.
In business applications, this kind of expression happens in many places in most programs. I understand fixed decimal arithmetic went out of favor with language designers a long time ago, but that shorted-sightedness is probably the single biggest reason that COBOL still exists as the significant programming language in banking application software. The IEEE/ISO, Microsoft and Rust understand this fundamental need. See
C# is major competition for Kotlin. It has had this feature for over a decade. Many who would otherwise write code in Java use C#, to Microsoft’s advantage. See
Operator overloading allows to use
+ instead of
add, no problem here. You can write Decimal128 type yourself if BigDecimal does not fit your case.
Yes, I’ve done that (I call the type Num).
But without a basic type, expressions are cumbersome.
My example from a previous post: x = (a * .95) + b / 12
var a = Num(1234)
var b = Num(5.678)
var x = a * Num(.95) + b / Num(12)
which is far better than Java, but is still more verbose than necessary. Kotlin/Swift and others have gone to extraordinary lengths to get rid of a few characters here and a few characters there. For business applications, d128 would save lots of boilerplate for this kind of programming. This really is low hanging fruit in the quest for succinctness.
With a d128 basic type, the code would be:
var a = 1234d
var b = 5.678d
var x = a * .95d + b / 12d
I strongly suggest to use
BigDecimal unless your code is extremely performance-sensitive to warrant implementation of your own type. For convenience, I’d suggest to add the following extension when your code is BidDecimal-heavy:
val Int.bd: BigDecimal get() = BigDecimal(this)
With this extension your example code becomes pretty good-looking:
var y = 2.bd var z = 3.bd var x = y + z
Also, it is worth noting here that the upcoming version of Kotlin will add to the set of operators that are supported for BigDecimal out-of-the box: https://github.com/Kotlin/KEEP/blob/master/proposals/stdlib/bignumber-operations.md
Thanks for the responses.
The future enhancements for big number operations appear to be generally what I am looking for. And I’d not thought of the .bd syntax for “literals”.
I don’t know how performant the Rust ‘d128’ type is but, in C#, the 128 bit ‘decimal’ type is notoriously slow compared to ‘double’ arithmetic which in turn is slower than integral arithmetic.
Consequently, developers of financial applications who are concerned about performance or memory usage in that language tend to just use the 64 bit ‘long’ type, working in cents, pence etc. and then dividing the final result by 100. That gives sufficient range for most applications, uses half the memory and is considerably faster.
You could, of course, do exactly the same in Kotlin which in my view would be a better solution than using BigDecimal which is massive over-kill for most business applications, uses more memory and is even slower than a dedicated 128 bit fixed decimal type would be.
VB6 (and COM generally) had a 64 bit ‘currency’ type whose underlying representation was a ‘long’, scaled to 4 decimal places, which was good enough (and quick enough) for most financial applications but, unfortunately, this wasn’t carried through to C# and the .NET version of VB.
If Kotlin were ever to get inline or value classes, then creating a Currency type might be a possible use case for it.
alanfo - yep, we’ve used that approach when necessary, although there are very few big data situations where it is really necessary - application source code is ugly when mixing integers with PennyLongs, but it is exact.
The software emulation won’t be as fast as Long in pennies, but will be just fine if no boxing/unboxing is needed for operations (including the Swift and Kotlin non-operation named “assignment”). The IBM Power 6 chips and Z10 chips do have hardware support. I suspect it will be introduced to x86 chips in the next decade. GCC already has an extension for its support, both by hardware and software emulation. Note the second link below concerning C++.
var x = a * Num(.95) + b / Num(12) I believe you can get rid of those
Num(.95) (or alternative syntax) by overloading
plus operator for different types (e.g. for
Double), so you could write
a * 0.95 + b / 12 if you have to write literals a lot. That said, in my experience all those magic numbers are extracted into config or constants, so it’s not needed and resulting code is quite readable.
vbezhenar - yes, in a perfect world, constants are extracted into some repository. That said, maintenance becomes a nightmare when details are hidden from the reader of code into some other XML/JSON/magic place. Unless a constant is truly malleable/configurable, IMO, it should not be extracted. For example, the number of months in a year. Simplicity wins in most cases, which implies no extraction.
Also it be nice nice if this worked -
var x = Num(0) ... x = 12 // error ... x = Num(12) // works
but that will require assignment to be overloadable, and since it’s not even an operation, I doubt that the Kotlin designers will make such a change.