Home > Back-end >  Rounding through type ascription
Rounding through type ascription

Time:01-03

I would like to introduce a custom class, say Decimal2, so that I would be able to round through type ascription:

val x: Decimal2 = 1.2345
// 1.24

So far I've tried this:

class Decimal2(val value: Double)

object Decimal2 {

  def apply(value: Double) =
    BigDecimal(value).
    setScale(2, BigDecimal.RoundingMode.HALF_UP).
    toDouble

  implicit def doubleToDecimal2(x: Double) = Decimal2(x)

}

val a: Decimal2 = 1.2345

But:

  1. the last line apparently fails.
  2. return type of apply method is Double, not Decimal2

How can I reach my goal?

CodePudding user response:

It looks like you have two requirements.

  1. Make Decimal2 class provides a method that returns a rounded Double value.
  2. Convert Double value to Decimal2 class

Here is a straightforward approach.

  case class Decimal2(value: Double){
    def toDouble: Double = BigDecimal(value).
      setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble
  }

  implicit class DoubleImprovement(val x: Double){
    def toDecimal2: Decimal2 = Decimal2(x)
  }
  
  println(1.2345.toDecimal2.toDouble)

CodePudding user response:

It's easy to do this with scala 3 opaque type. I would say even more, opaque type is made for this kind of thing.

// a Decimal2 is a Double with some restriction
opaque type Decimal2 = Double
object Decimal2 :
  def apply(d2 : Double) : Decimal2 = BigDecimal(d2).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble

// extension method to Decimal2 to have a back conversion to a full Double 
extension (d2 : Decimal2)
  def double : Double = d2

// implicit conversion for convenience
given doubleToDecimal2 : Conversion[Double, Decimal2] = Decimal2(_)

now you can use it like you want :

val d2 : Decimal2 = 1.23456 // 1.23
d2 // Decimal2
d2.double // double

CodePudding user response:

One way is by explicitly creating a new instance of Decimal2 from apply method.

object Decimal2 {
  def apply(value: Double): Decimal2 =
    new Decimal2(BigDecimal(value)
      .setScale(2, BigDecimal.RoundingMode.HALF_UP)
      .toDouble)

  implicit def doubleToDecimal2(x: Double): Decimal2 = Decimal2(x)
}

Another option is to tag the type:

trait RoundTo2
type Decimal2 = Double with RoundTo2

object Decimal2 {
  def apply(value: Double): Decimal2 = {
    BigDecimal(value)
      .setScale(2, BigDecimal.RoundingMode.HALF_UP)
      .toDouble
  }.asInstanceOf[Double with RoundTo2]
}
implicit def doubleToDecimal2(x: Double): Decimal2 = Decimal2(x)

val a: Decimal2 = 1.2345
  • Related