jueves, 29 de noviembre de 2012

Que tan perfectos son los cálculos matemáticos en la Computación?

Nota: El código se puede compilar y ver su salida en linea usando uno de los links en http://aluismarte.blogspot.com/p/herramientas-en-linea.html

Cuando empezamos a programar utilizamos los tipos de datos numéricos  con los cuales se nos enseñan las lógicas básicas para crear algoritmos y tener una mente ordenada, pero aveces aparecen casos muy particulares cuando trabajamos con los números; dígase cuando aplicamos un algoritmo de redondeo a números flotantes o cuando necesitamos más precisión en los cálculos de lo normal, pero a que me refiero explícitamente , es simple, cuando usamos datos numéricos flotantes estos carecen en sí mismos de precisión, aunque suene algo ilógico estos los usamos para tener más precisión, pero que hace que un numero flotante carezca de precisión?

Para responder a esa pregunta solo tenemos que entender que la computación es un gran puñado de bits (no bytes) en orden secuencial y lógico ordenado que permite tener diferentes opciones, pero recordando que estos bits solo pueden tener 2 estados consistentes y 1 no consistente, dígase 0 o 1 y en el estado no consistente sería que este carezca de existencia.

Como ya sabemos los bits tienen solo 2 valores, por lo que si no es 1 es cero, y los datos se guardan con una agrupación de estos, pero en los números tenemos singularidades en los flotantes, ya que estos tienen datos de 1 entero y datos para precisión (serían los números después del punto), por lo que entenderíamos que estos no tendrían un margen de error, pero si lo tienen!, si trabajamos con números que no son potencias de 2 estos generan un residuo importante lo cual puede hacer fallar un programa con una lógica impecable, pero me dejo de explicaciones y les muestro un ejemplo:

Este ejemplo es aplicable a todos los lenguajes que trabajo (C, C++, Java, C#, Python), aunque seguiré buscando aprender más lenguajes para probar estos casos.

#include <iostream>
using namespace std;

int main() {
    for(double i = 0.0 ; i < 2.0 ; i+=0.1) {
        if(i == 1.0) {
            cout << "Se supone que termino aqui." << endl;
            break;
        }
        cout << i << endl;
    }
    return 0;
}


Cuando corran este programa observaran algo curioso, pero si cambian el factor de suma de la variable i verán un efecto diferente, aunque este efecto es un caso raro, pero nos ayuda a hacer conciencia para que cada vez que trabajemos con flotantes usemos rango y no igualdades.

#include <iostream>
using namespace std;

int main() {
    for(double i = 0.0 ; i < 2.0 ; i+=0.125) {
        if(i == 1.0) {
            cout << "Se supone que termino aqui." << endl;
            break;
        }
        cout << i << endl;
    }
    return 0;
}

Aquí vemos el comportamiento de los flotantes, pero existen casos en los cuales se hallan perdido recursos debido a este error que no se contempla?, pues aquí hay unos ligeros ejemplos.

MIM-104 Patriot (1991): Es un misil antiaéreo que se utilizan también para la intercepción de misiles balísticos a modo de defensa. Durante la Guerra del Golfo en 1991 un Scud iraquí mató a 28 soldados al alcanzar un cuartel norteamericano ya que estos misiles fallaron. Se dictaminó que fue un error de software en el reloj del sistema que se había retrasado un tercio de segundo por haber estado activado 100 horas.

Ariane-5 (1996): Esta fue una misión ESA en el que el vuelo 501 la lanzadera se desvió y se partió durando únicamente 40 segundos en vuelo. El problema fue que el sistema de referencia inercial tratado con datos de punto flotante de 64 bits lo convertía a 16 bits con valores enteros. En una de esas operaciones el valor era demasiado grande provocando un desbordamiento aritmético en el hardware.

Chips Pentium (1994): Un error en el juego de instrucciones en los chip Pentium repercutió en la empresa $475 millones de dólares de costes en recogida y reenvío de procesadores. El error se reproducía al realizar una división con coma flotante en el que a partir del cuarto dígito decimal no daba valores correctos. Este fallo empezó a llamarse “bug de FDIV“.

Este ultimo caso fue un error de el chip, pero el problema fue que no controlaba correctamente un margen de precisión aceptable, lo cual provocó un error crítico y perdidas de dinero enormes.

Como conclusión, podemos afirmar que los números flotantes tienen error, pero que estos son controlados hasta un cierto punto para evitar problemas, y que si no se tiene conciencia de ello podemos crear un error lógico.

Descarga de código

Un solo link, con todos los códigos de los 5 lenguajes que trabajo.
http://adf.ly/FQO9f

martes, 27 de noviembre de 2012

Un arreglo curioso de tamaño cero.

Arreglo [0]

Nota: El código se puede compilar y ver su salida en linea usando uno de los links en http://aluismarte.blogspot.com/p/herramientas-en-linea.html

Como programadores siempre haremos uso de los arreglos para manejar datos, pero que pasa cuando rompemos la lógica y buscamos crear algo con dimensiones cero?.

Pues verá que los compiladores de C, C++, Python y Java permiten la creación de arreglos declarados en 0, así como su manipulación pero en C# esto solo permite su creación, pero no da la posibilidad de entrar 1 datos sin que de error de indice. La manipulación de este arreglo es curiosa y varía según el lenguaje al igual que su comportamiento.

Por ejemplo en C++ la salida de este arreglo singular da números que no son siquiera cercanos a los insertados en el mismo. Estos números sin sentido que nos ofrece la salida del programa podríamos buscarle algo de lógica si nos hacemos la idea de que eso sería acceso a ram del programa. Aunque esto no lo he podido probar, pero es una posibilidad existente que intentaré probar más adelante.


#include <iostream>
using namespace std;

int main() {
int arr[0];
int i,j, p;
cout << "Inserte el numero de datos a introducir en el arreglo" << endl;
cin >> p;
//Introducimos algunos datos en nuestro arreglo.
for(i = 0 ; i < p ; i++) {
arr[i] = i + 10;
}
cout << "Imprimimos a ver que sale" << endl;
//Imprimimos nuestros valores guardados.
for(j = 0 ; j < p ; j++) {
cout << arr[j] << endl;
}
return 0;
}


Después de correr este curioso programa, podría interesarle ver como se comportan estos en otros lenguajes y con el fin de facilitar la prueba podrán descargar los códigos para sus respectivos lenguajes a probar.

Links de descarga del código.

Lenguaje C
http://adf.ly/FL2Bh

Lenguaje C++
http://adf.ly/FL2Hp

Lenguaje C#
http://adf.ly/FL2J5

Lenguaje Python
http://adf.ly/FL2Mg

Lenguaje Java
http://adf.ly/FL2Ka