3
votes

Avertir ou éviter la division entière (entraînant une troncature) dans scala

Considérez

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

ou

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

 x / n

Les deux sont égaux .. 0 .. depuis la division entière entraîne une troncature.

Aussi: (c'est mon cas d'utilisation typique):

 1 / 2

Cela m'a mordu à plusieurs reprises quand cela se produit au milieu de longs calculs: la première impulsion est de vérifier quelles erreurs logiques ont été introduites. Ce n'est qu'après une certaine période de débogage et de grattage de tête que le vrai coupable survient.

Existe-t-il un moyen d'exposer ce comportement plus clairement - par exemple un avertissement ou un paramètre de langue (inconnu pour moi) ou une construction qui alerterait ou éviterait ce scénario intermittent?


2 commentaires

Cela ne répondra pas à votre question mais - en règle générale - lorsque vous commencez à faire de l'arithmétique, utilisez des Float , des Double ou même des BigDecimal s.


Le fait est que les entiers sont générés fréquemment - par des API ou surtout par des opérations de comptage telles que la cardinalité des collections. Je n’ai pas réellement choisi de définir Int .. en parlant de cela, je mettrai à jour la question pour montrer ce cas d’utilisation.


3 Réponses :


4
votes

À ma connaissance, le compilateur Scala ne semble pas fournir un indicateur d'avertissement qui pourrait vous permettre de lever un avertissement (documentation here ).

Cependant, si vous trouvez que l'effort en vaut la peine, utilisez Scalafix et écrivez votre propre règle personnalisée pour détecter les divisions entières et signaler des avertissements à ce sujet.

Ce qui suit est un court exemple de règle qui peut détecter la division entière sur les littéraux entiers:

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

Lorsqu'elle est exécutée sur le morceau de code suivant:

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


2 commentaires

bon travail! Je doute que je l'utilise directement mais cela vaut la peine de voter pour l'info


Scalafix voit-il les types inférés? Sinon, github.com/wartremover/wartremover peut être plus approprié.



1
votes

Toute opération de division peut entraîner une troncature ou un arrondi. Ceci est plus visible avec Int mais peut arriver avec tous les types numériques (par exemple 1.0 / 3.0 ). Tous les types de données ont une plage et une précision restreintes, de sorte que le résultat de tout calcul peut être ajusté pour s'adapter au type de données résultant.

Il n'est pas clair que l'ajout d'avertissements pour le cas spécifique de la division Int va aider. Il n'est pas possible de détecter tous ces problèmes et, dans certains cas, donner des avertissements peut conduire à un faux sentiment de sécurité. Cela va également provoquer de nombreux avertissements pour un code parfaitement valide.

La solution consiste à examiner attentivement tous les calculs d'un programme et à connaître les limites de portée et de précision de chaque opération. S'il y a un calcul sérieux impliqué, c'est une bonne idée de se familiariser avec l'analyse numérique.


0 commentaires

2
votes

Si l'opération / ne fonctionne pas pour vous, créez-en une qui fonctionne.

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

test:

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)
}


0 commentaires