¿Por qué hay tantos tipos numéricos (bit, int, float, double, long)?

9

Aprendí PHP, Java y C. Ahora tengo curiosidad por saber por qué hay tantos tipos de tipos de datos numéricos como bit, int, float, double y long. ¿Por qué no hacer solo un tipo para números?

¿Hay algún beneficio para esto? ¿Tal vez si usamos números enteros para contener números tan pequeños podemos ahorrar memoria?

GusDeCooL
fuente
66
Además de la respuesta de HorusKol: los tipos 'flotante' y 'entero' son inherentemente diferentes. Los flotadores pueden contener números muy grandes, pero a medida que aumenta el tamaño del número, la precisión disminuye. Esta imprecisión se debe a la forma en que se almacenan los flotadores. Por el contrario, el rango de valores que puede almacenar en un número entero es bastante limitado, pero el valor siempre es exacto, por lo que puede comparar valores mucho más fácilmente. Además, hay dos tipos diferentes de comportamiento con división: los enteros se 'truncan' al número entero más cercano automáticamente, los flotantes no. Cada uno de estos comportamientos son útiles para diferentes situaciones.
Kampu
Javascript solo tiene un tipo de número en la superficie.
Esailija
@kampu: En realidad, en muchos idiomas, los enteros pueden almacenar cualquier número siempre que la memoria (virtual) sea lo suficientemente grande como para representarlo.
Jörg W Mittag
1
@ JörgWMittag: Sin embargo, ese interrogador claramente está hablando de lenguajes estáticos, no de lenguajes dinámicos como Python, por ejemplo. CPython implementa el entero 'rango ilimitado' como una matriz de entradas de 32 bits, con el bit final en cada int utilizado para indicar si hay más bits por recorrer. Además, los enteros solo pueden almacenar cualquier número entero . Eso significa que un flotante con almacenamiento infinito puede almacenar valores con precisión (infinity aleph one), mientras que los enteros pueden almacenar valores solo con precisión ( infinity aleph zero ).
Kampu
@kampu: Dado que todos los números están representados por series de bits, incluso con almacenamiento infinito, siempre habrá un mapeo uno a uno entre números de punto flotante y enteros. Así que no creo que aleph one sea cuestionado.
VENIDO DEL

Respuestas:

17

Hay dos razones por las que debería preocuparse por los diferentes tipos de datos numéricos.

1. Ahorro de memoria

for(long k=0;k<=10;k++)
{
    //stuff
}

¿Por qué usar un largo cuando podría ser fácilmente un número entero o incluso un byte? De hecho, ahorraría varios bytes de memoria al hacerlo.

2. Los números de coma flotante y los números enteros se almacenan de manera diferente en la computadora

Supongamos que tenemos el número 22 almacenado en un número entero. La computadora almacena este número en la memoria en binario como:

0000 0000 0000 0000 0000 0000 0001 0110

Si no está familiarizado con el sistema de números binarios, esto se puede representar en notación científica como: 2 ^ 0 * 0 + 2 ^ 1 * 1 + 2 ^ 2 * 1 + 2 ^ 3 * 0 + 2 ^ 4 * 1 + 2 ^ 5 * 0 + ... + 2 ^ 30 * 0. El último bit puede o no usarse para indicar si el número es negativo (dependiendo de si el tipo de datos está firmado o no).

Esencialmente, es solo una suma de 2 ^ (lugar de bit) * valor.

Esto cambia cuando se refiere a valores que involucran un punto decimal. Supongamos que tiene el número 3.75 en decimal. Esto se conoce como 11.11 en binario. Podemos representar esto como una notación científica como 2 ^ 1 * 1 + 2 ^ 0 * 1 + 2 ^ -1 * 1 + 2 ^ -2 * 1 o, normalizado, como 1.111 * 2 ^ 2

Sin embargo, la computadora no puede almacenar eso: no tiene un método explícito para expresar ese punto binario (la versión del sistema de números binarios del punto decimal). La computadora solo puede almacenar 1's y 0's. Aquí es donde entra el tipo de datos de coma flotante.

Suponiendo que el tamaño de (flotante) es de 4 bytes, entonces tiene un total de 32 bits. Al primer bit se le asigna el "bit de signo". No hay flotadores ni dobles sin firmar. Los siguientes 8 bits se usan para el "exponente" y los 23 bits finales se usan como el "significado" (o a veces se lo denomina mantisa). Usando nuestro ejemplo 3.75, nuestro exponente sería 2 ^ 1 y nuestro significado sería 1.111.

Si el primer bit es 1, el número es negativo. Si no, positivo. El exponente se modifica por algo llamado "el sesgo", por lo que no podemos simplemente almacenar "0000 0010" como exponente. El sesgo para un número de coma flotante de precisión simple es 127, y el sesgo para una precisión doble (aquí es donde el tipo de datos doble recibe su nombre) es 1023. Los 23 bits finales están reservados para el significado. El significado es simplemente los valores a la DERECHA de nuestro punto binario.

Nuestro exponente sería el sesgo (127) + exponente (1) o representado en binario

1000 0000

Nuestro significado sería:

111 0000 0000 0000 0000 0000

Por lo tanto, 3.75 se representa como:

0100 0000 0111 0000 0000 0000 0000 0000

Ahora, veamos el número 8 representado como un número de coma flotante y como un número entero:

0100 0001 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 1000

¿Cómo demonios va a agregar la computadora 8.0 y 8? ¿O incluso multiplicarlos? La computadora (más específicamente, las computadoras x86) tienen diferentes porciones de la CPU que agregan números de punto flotante y números enteros.

cpmjr123
fuente
3
3) aunque rara vez es un problema: las operaciones en números más grandes que el tamaño de las palabras de la computadora son más lentas.
Loren Pechtel
6

Antes de que tuviéramos sistemas de gigabytes (o en sistemas embebidos modernos como Arduino), la memoria era muy importante y, por lo tanto, se implementaron métodos abreviados para especificar cuánta memoria ocuparía un número en particular, BIT es sencillo, originalmente ocuparía solo 1 bit de la memoria

Los otros tamaños y nombres de datos varían entre sistemas. En un sistema de 32 bits, INT (o MEDIUMINT) generalmente sería de 2 bytes, LONGINT sería de 4 bytes y SMALLINT sería de un solo byte. Los sistemas de 64 bits pueden tener LONGINT establecido en 8 bytes.

Incluso ahora, especialmente en aplicaciones de bases de datos o programas que tienen múltiples instancias ejecutándose en servidores (como scripts del lado del servidor en sitios web), debe tener cuidado con lo que elige. Elegir un entero de 2, 4 u 8 bytes de ancho para almacenar valores entre 0 y 100 (que puede caber en un byte) es increíblemente inútil si tiene una tabla de base de datos con millones de registros.

Más información: https://en.wikipedia.org/wiki/Integer_(computer_science)

HorusKol
fuente
buena respuesta +1.
Vinay
77
No solo 'atrás antes', sino también 'ahora cuando un sistema es pequeño'. En un dispositivo del tamaño de Arduino, uno tiene que ser económico.
9000
1
¿Qué sistema usó solo 1 bit para almacenar un bit? los bits generalmente no son directamente direccionables
jk.
1
eso es cierto en muchas arquitecturas, pero los bits eran directamente direccionables en sistemas realmente antiguos e incluso en algunos sistemas embebidos más recientes (algunos controladores que programé hace solo 10 años trabajaban con bits, esos solo tenían alrededor de 64 ubicaciones direccionables de anchos específicos). Hoy en día, supongo que los compiladores lo resuelven y los colocan en conjuntos de bytes.
HorusKol
Creo que el factor primordial es la capacidad y el rendimiento de la CPU en lugar de los problemas de memoria
James
4

Además de los excelentes puntos de cpmjr123 sobre la escasez de memoria y la precisión y las compensaciones de rango, también es potencialmente una compensación de CPU.

La mayoría de las máquinas modernas tienen hardware especial para realizar operaciones de punto flotante llamado FPU. También hay sistemas que no tienen FPU (en la actualidad, estos son típicamente dispositivos integrados pequeños), por lo tanto, dependiendo de su hardware de destino, no tendría que usar ningún tipo de punto flotante o una biblioteca de punto flotante de software. Incluso si su máquina tiene una FPU, históricamente hubo diferencias en las funciones que podría proporcionar. Cualquier función que no se realice en hardware debería realizarse en software (o evitarse)

Hacer cálculos de coma flotante en el software se realiza realizando muchas operaciones más simples que el hardware admite. Por lo tanto, también obtienes una posible compensación de velocidad.

jk.
fuente
4

Quizás lo más importante es que realmente hay tres tipos de números básicos diferentes.

entero, decimal fijo y coma flotante.

Todos se comportan de manera diferente.

Una operación simple como 7/2 podría dar respuestas de 3, 3.50 y 3.499 dependiendo del tipo de datos utilizado.

"decimal fijo" es el tipo de Cenicienta, solo se admite de forma nativa en algunos idiomas como COBOL y VisualBasic. Es de poco interés para los científicos informáticos, pero es vital para cualquiera que presente un conjunto de cuentas o calcule el impuesto a las ventas en una factura.

James Anderson
fuente
Los separaría de manera diferente: números discretos, números aproximados y anillos algebraicos envolventes. Ejemplos típicos en C serían int, floaty unsigned int, respectivamente. Los tipos de punto fijo son una subcategoría de tipos discretos, pero los anillos algebraicos son fundamentalmente diferentes de los números [debido a la confusión con respecto a los tipos sin signo en C se debe al hecho de que se comportan principalmente como anillos en lugar de números, pero no son bastante consistentes] .
supercat
3

¿Hay algún beneficio que lo hagan?

Por supuesto. Hay beneficios En el mundo de las computadoras, la memoria es una de las cosas más importantes a tener en cuenta. ¿De qué sirve tener una memoria de 2kb cuando los datos pueden caber en menos de 1kb? . Las optimizaciones deberían estar allí. Si usa más memoria, obviamente mata la velocidad de su computadora en un punto. ¿De verdad te gusta tenerlo? No cierto ...?

int - 2 bytes (16 bits)

long - 4 bytes (32 bits)

long long - 8 bytes (64 bits)

float - 4 bytes

No solo la memoria, sino también la organización del tipo de números. para una instancia de coma flotante. La precisión es muy importante y, obviamente, deberíamos tener un tipo que pueda darnos más precisión.

Si consideramos los viejos tiempos, teníamos muy poca memoria, como sabrán. Para guardarlo y usarlo sabiamente, tuvimos estas diferencias. Y mucho más si continúas y tratas de buscar en Google. Espero que esto ayude.

Vinay
fuente
3

los números enteros y reales (flotante, doble) son tipos conceptualmente diferentes con diferentes conjuntos de operaciones y propiedades intrínsecas.

Los enteros son enumerables pero los flotantes no, etc.

De hecho, el número flotante / doble es una estructura que combina dos campos enteros: mantisa y exponente. Los números complejos (que excluyó de la consideración) son aún más, bueno, complejos.

Cualquier lenguaje práctico debe tener al menos números enteros y flotantes como tipos distintos, operaciones demasiado diferentes en ellos.

c-smile
fuente
No estoy familiarizado con los "números complejos" que has mencionado. ¿Puedes explicar más?
cpmjr123
Soy consciente de los números complejos en forma de a + bi. Estaba pidiendo más información sobre cómo la computadora almacena números complejos. Que yo sepa, no hay tipos de datos primitivos que respalden esto.
cpmjr123
Los números complejos generalmente se almacenan como dos valores de coma flotante, a saber, su a(parte real) y b(parte imaginaria). La CPU generalmente no implementa soporte nativo para operaciones en números complejos, aunque la CPU puede implementar instrucciones aceleradas de adición múltiple para operaciones en pares de valores, como (a b + c d) y (a b-c d).
rwong
1
Además, muchos lenguajes tienen algunos tipos cuyo comportamiento se define en gran medida como el de un anillo algebraico envolvente (p. Ej., Si una variable de tipo uint16_tcontiene 65535, al incrementarla se mantendrá 0). Idealmente, los idiomas tendrían tipos separados para representar anillos y números algebraicos envolventes (permitiendo que los números que se desbordan queden atrapados, mientras que permite que el código realice fácilmente operaciones en cosas que se espera que se envuelvan).
supercat
-1

Además del hecho de que los tipos de punto flotante se comportan completamente diferentes de los tipos enteros, quiero dar un ejemplo más extremo de por qué el tamaño por número realmente importa.

Imagine que desea ordenar una matriz (larga). Por ejemplo en C:

int numbers[100000000];

Entonces aquí tenemos 100 millones de números.

Si cada número tiene solo un byte de longitud (por lo que se usa en unsigned charlugar de int), entonces se necesitan 100 millones de bytes de espacio.

Si usa double, entonces esto suele ser de 8 bytes por número, por lo que 800 millones de bytes de espacio.

Entonces, cada vez que opera con muchos objetos (números en este ejemplo), el tamaño por objeto (tamaño por número en este ejemplo) realmente importa.

Ingo Blackman
fuente