Avez-vous déjà essayé de résumer tous les nombres de 1 à 2 000 000 dans votre langage de programmation préféré? Le résultat est facile à calculer manuellement: 2 000 001 000 000, soit 900 fois la valeur maximale d’un nombre entier non signé de 32 bits.
C # imprime -1453759936
- une valeur négative! Et je suppose que Java fait la même chose.
Cela signifie que certains langages de programmation courants ignorent le débordement arithmétique par défaut (en C #, il existe des options cachées pour le changer). C'est un comportement qui me semble très risqué, et le crash d'Ariane 5 n'a-t-il pas été causé par un tel débordement?
Alors: quelles sont les décisions de conception derrière un comportement aussi dangereux?
Modifier:
Les premières réponses à cette question expriment les coûts excessifs de la vérification. Exécutons un court programme C # pour tester cette hypothèse:
Stopwatch watch = Stopwatch.StartNew();
checked
{
for (int i = 0; i < 200000; i++)
{
int sum = 0;
for (int j = 1; j < 50000; j++)
{
sum += j;
}
}
}
watch.Stop();
Console.WriteLine(watch.Elapsed.TotalMilliseconds);
Sur ma machine, la version vérifiée prend 11015 ms, tandis que la version non vérifiée prend 4125 ms. C'est-à-dire que les étapes de vérification prennent presque deux fois plus longtemps que l'ajout des nombres (au total, trois fois le temps initial). Mais avec les 10 000 000 000 de répétitions, le temps nécessaire à un contrôle est toujours inférieur à 1 nanoseconde. Il peut y avoir une situation où cela est important, mais pour la plupart des applications, cela n’a aucune importance.
Edit 2:
J'ai recompilé notre application serveur (un service Windows analysant les données reçues de plusieurs capteurs, avec un certain nombre de calculs complexes) avec le /p:CheckForOverflowUnderflow="false"
paramètre (normalement, j'active le contrôle de débordement) et je l'ai déployé sur un périphérique. La surveillance de Nagios montre que la charge moyenne du processeur est restée stable à 17%.
Cela signifie que l'impact sur les performances constaté dans l'exemple ci-dessus n'est absolument pas pertinent pour notre application.
(1..2_000_000).sum #=> 2000001000000
. Un autre de mes langues préférées: sum [1 .. 2000000] --=> 2000001000000
. Pas mon préféré: Array.from({length: 2000001}, (v, k) => k).reduce((acc, el) => acc + el) //=> 2000001000000
. (Pour être juste, le dernier triche.)
Integer
à Haskell est de précision arbitraire, il contiendra n'importe quel nombre tant que vous ne manquerez pas de RAM allouable.
But with the 10,000,000,000 repetitions, the time taken by a check is still less than 1 nanosecond.
c'est une indication de la boucle en cours d'optimisation. Cette phrase contredit également les chiffres précédents qui me paraissent très valables.
checked { }
section pour marquer les parties du code devant effectuer des contrôles de débordement arithmétique. Cela est dû à la performance