WestCoastProjects
WestCoastProjects

Reputation: 63042

Warn about or avoid integer division (resulting in truncation) in scala

Consider

 1 / 2

or

 val x: Int = ..
 val n: Int = ..

 x / n

Both of these equal .. 0 .. since integer division results in truncation.

Also: (this is my typical use case):

val averageListSize =  myLists.map(_.length).sum()/myLists.length

This has bitten me a few times when it occurs in the middle of long calculations: the first impulse is to check what logical errors have been introduced. Only after some period of debugging and head scratching does the true culprit arise.

Is there any way to expose this behavior more clearly - e.g. a warning or some (unknown-to-me) language setting or construction that would either alert to or avoid this intermittent scenario?

Upvotes: 3

Views: 673

Answers (3)

jwvh
jwvh

Reputation: 51271

If the / op doesn't work for you, make one that does.

implicit class Divider[N](numer :N)(implicit evN :Numeric[N]) {
  def /![D](denom :D)(implicit evD :Numeric[D]) :Double =
    evN.toDouble(numer) / evD.toDouble(denom)
}

testing:

1   /! 2    //res0: Double = 0.5
5.2 /! 2    //res1: Double = 2.6
22  /! 1.1  //res2: Double = 20.0
2.2 /! 1.1  //res3: Double = 2.0

Upvotes: 2

Tim
Tim

Reputation: 27356

Any division operation can result in truncation or rounding. This is most noticeable with Int but can happen with all numeric types (e.g. 1.0/3.0). All data types have a restricted range and accuracy, and so the result of any calculation may be adjusted to fit into the resulting data type.

It is not clear that adding warnings for the specific case of Int division is going to help. It is not possible to catch all such issues, and giving warnings in some cases may lead to a false sense of security. It is also going to cause lots of warnings for perfectly valid code.

The solution is to look carefully at any calculations in a program and be aware of the range and accuracy limitations of each operation. If there is any serious computation involved it is a good idea to get a basic grounding in Numerical Analysis.

Upvotes: 1

stefanobaghino
stefanobaghino

Reputation: 12794

To the best of my knowledge, the Scala compiler does not seem to provide a warning flag that could allow you to raise a warning (documentation here).

What you could do, however, if you find the effort worth it, is using Scalafix and write your own custom rule to detect integer divisions and report warnings about it.

The following is a short example of a rule that can detect integer division on integer literals:

import scalafix.lint.{Diagnostic, LintSeverity}
import scalafix.patch.Patch
import scalafix.v1.{SemanticDocument, SemanticRule}

import scala.meta.inputs.Position
import scala.meta.{Lit, Term}

class IntDivision extends SemanticRule("IntDivision") {

  override def fix(implicit doc: SemanticDocument): Patch =
    doc.tree.collect({
      case term @ Term.ApplyInfix((_: Lit.Int, Term.Name("/"), Nil, _: List[Lit.Int])) =>
        Patch.lint(new Diagnostic {
          override final val severity: LintSeverity = LintSeverity.Warning
          override final val message: String = "Integer division"
          override final val position: Position = term.pos
        })
    }).asPatch

}

When run on the following piece of code:

object Main {
  def main(args: Array[String]): Unit = {
    println(1 / 2)
  }
}

Scalafix will produce the following warning:

[warn] /path/to/Main.scala:3:13: warning: [IntDivision] Integer division
[warn]     println(1 / 2)
[warn]             ^^^^^

Upvotes: 4

Related Questions