suish
suish

Reputation: 3343

Write BigDecimal calculation as if it's just a usual Four arithmetic operations in Scala

I'm currently working on Scala and getting surprised how good it is. One of the great points I think is how it handles BigDecimal type.Once defining a BigDecimal You can use it almost as if it is Int or Double

val foo:BigDecimal = 0.1
println(foo + 0.2) // 0.3

There is no need to use .multiply() or .add() but simply + or * to handle BigDecimal Calculation(I assume BigDecimal's +/* end up calling them inside of it though).It's really easy to read even if a calculation gets complicated EXCEPT one thing that You have to declare first calculated value as BigDecimal.

println(0.3 * (BigDecimal(0.1) + 0.2))

My question is, How Can I write above code like this?

println(0.3 * (0.1 + 0.2))

I assume that this requires to override numeric type literals.

Thanks in advance.

Upvotes: 3

Views: 155

Answers (2)

Jörg W Mittag
Jörg W Mittag

Reputation: 369448

Scala doesn't allow overriding literals, unfortunately.

One thing you could easily do from within the language proper, would be to provide your own StringContext for string interpolation:

implicit class BigDecimalStringContext(val sc: StringContext) extends AnyVal {
  def d() = BigDecimal(sc.parts.head)
}

val test = d"0.1"
// => test: scala.math.BigDecimal = 0.1

I believe Macros don't allow you to override literals.

Scala-Virtualized is a fork of Scala, where (almost) everything (including control structures and object construction) translates into a method call and can thus be overloaded. Unfortunately, almost everything does not include overloading literals.

You might, however, be able to do something with a Compiler Plugin.

Upvotes: 2

Kolmar
Kolmar

Reputation: 14224

I don't think you can override numeric literals. What you can do is simplify the syntax a bit using string interpolation or adding methods to Double.

// With string interpolation
implicit class BigDecimalStringContext(private val sc: StringContext) extends AnyVal {
  def d(args: Any*): BigDecimal = {
    val stringRepr = sc.s(args: _*)
    BigDecimal(stringRepr)
  }
}

println(d"0.3" * (d"0.1" + d"0.2"))
println(0.3 * (d"0.1" + 0.2))


// Pimp my library 
implicit class DoubleOps(private val d: Double) extends AnyVal {
  def bd = BigDecimal(d)
}

println(0.3.bd * (0.1.bd + 0.2.bd))
println(0.3 * (0.1.bd + 0.2))

The first method is safer, because the second one tries to convert a Double, and thus may lose precision:

scala> 0.123456789012345678901234567890.bd * (0.1.bd + 0.2.bd)
res1: scala.math.BigDecimal = 0.037037036703703704

scala> d"0.123456789012345678901234567890" * (d"0.1" + d"0.2")
res2: scala.math.BigDecimal = 0.0370370367037037036703703703670

Upvotes: 6

Related Questions