¿Alguna vez trató de resumir todos los números del 1 al 2,000,000 en su lenguaje de programación favorito? El resultado es fácil de calcular manualmente: 2,000,001,000,000, que es aproximadamente 900 veces mayor que el valor máximo de un entero de 32 bits sin signo.
C # imprime -1453759936
: ¡un valor negativo! Y supongo que Java hace lo mismo.
Eso significa que hay algunos lenguajes de programación comunes que ignoran el desbordamiento aritmético de forma predeterminada (en C #, hay opciones ocultas para cambiar eso). Ese es un comportamiento que me parece muy arriesgado, y ¿no fue el accidente de Ariane 5 causado por tal desbordamiento?
Entonces: ¿cuáles son las decisiones de diseño detrás de un comportamiento tan peligroso?
Editar:
Las primeras respuestas a esta pregunta expresan los costos excesivos de la verificación. Ejecutemos un breve programa de C # para probar esta suposición:
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);
En mi máquina, la versión comprobada tarda 11015 ms, mientras que la versión no comprobada tarda 4125 ms. Es decir, los pasos de verificación toman casi el doble de tiempo que sumar los números (en total 3 veces el tiempo original). Pero con las 10,000,000,000 de repeticiones, el tiempo que demora un cheque sigue siendo inferior a 1 nanosegundo. Puede haber una situación en la que eso sea importante, pero para la mayoría de las aplicaciones, eso no importará.
Edición 2:
Volví a compilar nuestra aplicación de servidor (un servicio de Windows que analiza los datos recibidos de varios sensores, involucrando bastantes cálculos numéricos) /p:CheckForOverflowUnderflow="false"
parámetro (normalmente, enciendo la verificación de desbordamiento) y lo implementé en un dispositivo. El monitoreo de Nagios muestra que la carga promedio de CPU se mantuvo en 17%.
Esto significa que el impacto en el rendimiento que se encuentra en el ejemplo anterior es totalmente irrelevante para nuestra aplicación.
(1..2_000_000).sum #=> 2000001000000
. Otra de mis favoritas idiomas: sum [1 .. 2000000] --=> 2000001000000
. No es mi favorito: Array.from({length: 2000001}, (v, k) => k).reduce((acc, el) => acc + el) //=> 2000001000000
. (Para ser justos, el último es hacer trampa.)
Integer
en Haskell es de precisión arbitraria, contendrá cualquier número siempre que no se quede sin RAM asignable.
But with the 10,000,000,000 repetitions, the time taken by a check is still less than 1 nanosecond.
eso es una indicación del bucle que se está optimizando. También esa oración contradice números anteriores que me parecen muy válidos.
checked { }
sección para marcar las partes del código que deben realizar comprobaciones de desbordamiento aritmético. Esto se debe al rendimiento