Cristina HG
Cristina HG

Reputation: 672

Convert Numeric type to Double without parameter

I am trying to implement a generic method to compute the mean of any kind of sequence (for example: List, Array) which contains any kind of numeric values (Int, Float, Double...), like this:


 def mean[T <: Numeric[T]](data:Seq[T])(implicit number: Numeric[T]): T = {
      data.foldLeft(number.zero)(number.plus) / data.size
  }

However, the division operation cannot be resolved. That is because the Numeric type does not have this operation defined (from the ScalaDoc). I want to convert it to double before proceeding with the division, but the method toDouble(x:T) from Numeric type expects a param. I have seen there is a type member for the Numeric[T] called NumericOps that does implement the toDouble method without receiving any param. Could I call this method.. somehow?

Upvotes: 2

Views: 412

Answers (3)

Here is an example using Fractional, it will preserve the correct precision of the input numbers, and does only one traversal of the data. However, do note that this only works for types that have a "precise" division, like Float, Double & BigDecimal. But does not work for numeric types like Int or Long.

def mean[T](data: Iterable[T])(implicit N: Fractional[T]): T = {
  import N._

  val remaining = data.iterator

  @annotation.tailrec
  def loop(sum: T, count: Int): T =
    if (remaining.hasNext)
      loop(sum + remaining.next(), count + 1)
    else if (count == 0)
      zero
    else
      sum / fromInt(count)

  loop(zero, 0)
}

This was tested on Scala 2.13.

Upvotes: 4

Mario Galic
Mario Galic

Reputation: 48400

If Double precision is sufficient, try

def mean[T](data: Seq[T])(implicit number: Numeric[T]): Double = {
  import number._
  val sum = data.foldLeft(zero)(plus) 
  toDouble(sum) / data.size
}

mean(Seq(1,2,3,4)) // 2.5

or using Fractional (but it will not work for Ints)

def mean[T](data: Seq[T])(implicit number: Fractional[T]): T = {
  import number._
  val sum = data.foldLeft(zero)(plus)
  div(sum, fromInt(data.size))
}

mean(Seq(1.0,2,3,4)) // 2.5
mean(Seq(1,2,3,4))   // error: could not find implicit value for parameter number: Fractional[Int]

Upvotes: 3

James Black
James Black

Reputation: 41858

Unless you have to use Numeric why not just use Fractional, as that adds a div operation to Numeric.

You may find this of interest as they talk about the different options:

https://stackoverflow.com/a/40351867/67566

Upvotes: 2

Related Questions