I'm trying to convert a long amount in euro cents ...
# getting-started
c
I'm trying to convert a long amount in euro cents to a readable value. I'm not European and I see conflicting information on this. Here is my ext func
Copy code
fun Long.euroCentsToString(): String {
    val format = NumberFormat.getCurrencyInstance()
    format.maximumFractionDigits = 2
    format.currency = Currency.getInstance("EUR")

    return format.format(this/100)
}
If I call
849999L.euroCentsToString()
I get a result of
€8,499.00
I thought what I'd get is
€8.499,99
So Basically three issues: 1. I thought europeans use
.
to separate thousands from hundreds 2. I thought europeans use
,
to designate the start of fractional (cents) 3. I thought I'd see 99 as the cents and not 00.
t
My guess is it uses the local (e.g. where you are) formatting rules.
I'm pretty sure you can override that.
Also, you likely have to specific rounding rules. It probably has more than 2 digits after the decimal palce. So went you tell it to display just 2 digits, it rounds (because you didn't tell it otherwise). so .999 becomes .00
c
Interesting. Yeah. if I do val format = NumberFormat.getCurrencyInstance(Locale.Italy) for example then I get
8.499,00 €
the trailing euro symbol is interesting, but it proves your point.
t
what type is
this
?
c
It's an extension function. so this is referring to the Long
t
It's common to have the symbol after the number in some countries.
👍 1
c
I tried setting RoundingMode.Unnecessary, but it still seemed to turn the 99 cents into 00.
t
oh. yeah. so if you have 12345 in a long, and you divide it by 100 you end up with 123 So when you ask that to be printed with 2 decimal points of resolution it prints out 123.00
c
ah okay. so dividing it by 100 is the mistake. lemme try that
e
try BigDecimal+scale
c
Hm. Now I end up with 849.999,00, but the original number in cents is "849999"
t
so, it's expecting a # in whole units (euros, or dollars or whatever), and you're giving it a unit in cents.
so what it's printing out seems correct.
c
Copy code
return format.format(this/100.0)
fixed it. So I needed the 100.0 not 100
t
yeah, that's converting it to a float (or double) before it does the divide.
e
please don't use floating point with currency. fixed point will not have any rounding issues
t
I'm not sure what your doing, but finical system don't use floating point math for currencys
e
hence my BigDecimal suggestion immediately previously
c
Yeah. My BE sends me values in cents
so that theres no floating point
I didn't know to format it, I still needed BigDecimal. I'm aware of why I would use BigDecimal in other scenarios.
But turning 599L to a string of "$5.99" for example would have never occured to me that I need a BigDecimal.
e
Copy code
BigDecimal(599L.toBigInteger(), 2) == BigDecimal("5.99")
BigDecimal is exact, the scaling is done on powers of 10. it feels like overkill but there isn't another fixed point number type built in
👍 1
t
You can keep your number in Ints/Longs if you like. But then you have to do all the "prettyPrint" stuff by hand. mod by 100 to get the cents, and print that seperatly to the dollars... etc. But only the most latency sensitive systems do that... And since your printing it... you by definition don't care about the latency... so just use BigDecimal. It's much easier to work with.
c
So what @ephemient posted makes sense, but now how do I put that into a number formatter?
t
Same way. I think number formats will work on a BigDecimal (or BigDecimal has it's on formatter, I can't rememeber0
e
Copy code
NumberFormat.getCurrencyInstance(Locale.ITALY).format(BigDecimal(849999L.toBigInteger(), 2)) == "8.499,99 €"
NumberFormat.getCurrencyInstance(Locale.forLanguageTag("it-IT-u-cu-USD")).format(BigDecimal(849999L.toBigInteger(), 2)) == "8.499,99 USD"
NumberFormat.getCurrencyInstance(Locale.forLanguageTag("en-US-u-cu-EUR")).format(BigDecimal(849999L.toBigInteger(), 2)) == "€8,499.99"
c
It seems like this works. Let me know if its bad
Copy code
fun Long.centsToString(): String {
  val format = NumberFormat.getCurrencyInstance(Locale.ITALY)
  format.maximumFractionDigits = 2
  format.currency = Currency.getInstance("EUR")

  return format.format(BigDecimal(this).divide(BigDecimal(100.0)))
}
t
seems fine to me.
e
just use the constructor that lets you set the scale though?
c
Makes sense to me? The
toBigInteger
) was unexpected to me tho
Copy code
fun Long.centsToString(): String {
  val format = NumberFormat.getCurrencyInstance(Locale.ITALY)
  format.currency = Currency.getInstance("EUR")
  return format.format(BigDecimal(this.toBigInteger(), 2))
}
Ended up with this now.
e
BigDecimal is a wrapper around BigInteger + scale (e.g. where the decimal point is)
c
interesting. okay. Well I have the above extension for now. Seems like it'll get the job done and is accurate (no weird floating point issues). Cheers.
e
I edited my previous example to show that you can embed currency into the locale itself, as well
👍 1
662 Views