Já tentou resumir todos os números de 1 a 2.000.000 na sua linguagem de programação favorita? O resultado é fácil de calcular manualmente: 2.000.001.000.000, que são 900 vezes maiores que o valor máximo de um número inteiro de 32 bits não assinado.
C # imprime -1453759936
- um valor negativo! E eu acho que Java faz o mesmo.
Isso significa que existem algumas linguagens de programação comuns que ignoram o Arithmetic Overflow por padrão (em C #, existem opções ocultas para alterar isso). Esse é um comportamento que me parece muito arriscado, e a queda do Ariane 5 não foi causada por um excesso tão grande?
Então: quais são as decisões de design por trás de um comportamento tão perigoso?
Editar:
As primeiras respostas a esta pergunta expressam os custos excessivos da verificação. Vamos executar um pequeno programa C # para testar esta suposição:
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);
Na minha máquina, a versão marcada leva 11015ms, enquanto a versão desmarcada leva 4125ms. Ou seja, as etapas de verificação levam quase o dobro do tempo de adição dos números (no total, três vezes a hora original). Porém, com as 10.000.000.000 de repetições, o tempo gasto em um cheque ainda é menor que 1 nanossegundo. Pode haver uma situação em que isso é importante, mas para a maioria dos aplicativos isso não importa.
Edição 2:
Eu recompilei nosso aplicativo de servidor (um serviço do Windows analisando dados recebidos de vários sensores, bastante processamento de número envolvido) com o /p:CheckForOverflowUnderflow="false"
parâmetro (normalmente, eu ativei a verificação de estouro) e implantei em um dispositivo. O monitoramento do Nagios mostra que a carga média da CPU ficou em 17%.
Isso significa que o desempenho encontrado no exemplo inventado acima é totalmente irrelevante para a nossa aplicação.
(1..2_000_000).sum #=> 2000001000000
. Outra das minhas línguas preferidas: sum [1 .. 2000000] --=> 2000001000000
. Não o meu favorito: Array.from({length: 2000001}, (v, k) => k).reduce((acc, el) => acc + el) //=> 2000001000000
. (Para ser justo, o último é batota.)
Integer
em Haskell é de precisão arbitrária; ele manterá qualquer número enquanto você não ficar sem RAM alocável.
But with the 10,000,000,000 repetitions, the time taken by a check is still less than 1 nanosecond.
isso é uma indicação de que o loop está sendo otimizado. Também essa frase contradiz números anteriores que me parecem muito válidos.
checked { }
seção para marcar as partes do código que devem executar as verificações do Arithmetic Overflow. Isto é devido ao desempenho