2017-05-12 3 views
8

Je suis confus avec la façon dont Scala gère la division par zéro. Voici un extrait de code REPL.Scala division par zéro donne des résultats différents

scala> 1/0 
java.lang.ArithmeticException:/by zero 
    ... 33 elided 

scala> 1.toDouble/0.toDouble 
res1: Double = Infinity 

scala> 0.0/0.0 
res2: Double = NaN 

scala> 0/0 
java.lang.ArithmeticException:/by zero 
    ... 33 elided 

scala> 1.toInt/0.toInt 
java.lang.ArithmeticException:/by zero 
    ... 33 elided 

Comme vous pouvez le voir dans l'exemple ci-dessus, selon la façon dont vous divisez par zéro, vous obtenez une des options suivantes:

  • « java.lang.ArithmeticException:/par zéro »
  • "Double = NaN"
  • "Double = Infinity"

Cela rend le débogage particulièrement difficile tout en traitant des données de caractéristiques inconnues. Quel est le raisonnement derrière cette approche, ou même une meilleure question, comment gérer la division par zéro d'une manière unifiée dans Scala?

+1

Je pense que cela a à voir avec les différents types de données. Lorsque vous effectuez des calculs différents, mais liés, essayez d'utiliser toujours les mêmes types de données. Par exemple: Double, Int, etc. – Titulum

+0

Vous avez peut-être affaire à des données de caractéristiques inconnues, mais dans une langue à typage statique comme Scala, vous n'avez pas affaire à des données de type inconnu. –

+0

@AlexeyRomanov Je comprends ce que tu veux dire. Cependant, je suppose que la plupart des gens seraient d'accord que ce genre d'approche est sujette à des bugs très sales et très fastidieux à gérer dans chaque opération arithmétique que vous écrivez. – Ahmedov

Répondre

17

Tout dépend de la division par zéro des règles pour différents types.

0/0 est une division entière par zéro (comme les deux arguments sont des entiers), et qui est nécessaire pour lancer une java.lang.ArithmeticException.

1.toDouble/0.toDouble est une division à virgule flottante par zéro avec un numérateur positif, et qui est nécessaire pour évaluer à +Infinity.

0.0/0.0 est une division en virgule flottante par zéro avec un numérateur nul, et qui est nécessaire pour évaluer à +NaN. Le premier est une convention Java et Scala, les deux autres sont des propriétés du point flottant IEEE754, ce que Java et Scala utilisent tous les deux.

2

Doubles et Floats sont floating-point valeurs (more here) qui peuvent être représentées comme +Infinity-Infinity et NaN tel que défini dans la norme IEEE 754.
Integers sont fixed numbers qui ne dispose d'aucun moyen d'indiquer explicitement des données non valides, ainsi ils jettent exceptions
solution unifiée à ce serait d'utiliser la méthode getOrElse sur Try

Try(x/y).getOrElse(0) 

Si vous voulez récupérer uniquement sur ArithmeticException vous pouvez utiliser recover et get

Try(x/y).recover{ case _: ArithmeticException => 0 }.get 

recover vous permet de convertir Failure-Success
Vous pouvez également utiliser Try-Option retourner « aucun résultat » sans montrer exception

Try(x/y).toOption 
+1

Merci. Votre réponse est très utile dans la mesure où elle explique les solutions possibles. – Ahmedov

+0

@Ahmedov, merci pour le compliment. –

1

Vous pouvez utiliser des fonctions partielles pour quelque chose comme ça.Par exemple:

object MyObject { 
    def main(args: Array[String]) { 

     println(safeDiv.isDefinedAt(1.0, 1.0)) // true 
     println(safeDiv.isDefinedAt(1.0, 0.0)) // false 
     println(safeDiv(1.0, 1.0))    // 1.0 
     println(safeDiv(1.0, 0.0))    // crash 

    } 

    def safeDiv: PartialFunction[(Double, Double), Double] = { 
     case(a,b) if b != 0.0 => a/b 

    } 
} 

Les fonctions partielles permettent de vérifier si la fonction est définie pour une entrée donnée. Dans l'exemple ci-dessus, j'ai dit que la fonction safeDiv n'est pas définie si le diviseur est 0.0. Par conséquent, vous pouvez vérifier si la fonction sera exécutée compte tenu de l'entrée. La vérification n'est pas nécessaire, cependant safeDiv(1.0, 0.0) ne s'exécutera pas.

fonctions partielles est votre ami contre quelque chose comme ceci:

scala> (1.0/0.0).toInt 
res22: Int = 2147483647 
+0

Génial. Je pense que ça deviendra utile – Ahmedov