¿Por qué necesitas flotador / doble?

29

Estaba viendo http://www.joelonsoftware.com/items/2011/06/27.html y me reí de la broma de Jon Skeet acerca de que 0.3 no era 0.3. Personalmente nunca tuve problemas con flotadores / decimales / dobles, pero recuerdo que aprendí 6502 muy temprano y nunca necesité flotadores en la mayoría de mis programas. La única vez que lo usé fue para gráficos y matemáticas donde los números inexactos estaban bien y la salida era para la pantalla y no para ser almacenada (en un archivo de base de datos) o dependiente.

Mi pregunta es, ¿dónde están los lugares donde solía usar flotantes / decimales / dobles? Así que sé tener cuidado con estas trampas. Con dinero uso valores largos y valores de almacenamiento por centavo, para la velocidad de un objeto en un juego agrego entradas y divido (o cambio de bits) el valor para saber si necesito mover un píxel o no. (Hice un movimiento de objetos en los 6502 días, no tuvimos divisiones ni flotadores, pero tuvimos turnos).

Así que tenía mucha curiosidad.

FrustratedWithFormsDesigner
fuente
10
porque es muy importante que el interés que pague en mi hipoteca siga siendo 12.6 y se convierta en 13 solo porque 13 es un buen número redondo.
Chani
1
"Aprendí 6502 muy temprano y nunca necesité flotantes en la mayoría de mis programas ... para la velocidad de un objeto, agrego ints y divido el valor para saber si mover un píxel o no". Estas son formas muy inusuales de realizar estas tareas en la práctica moderna, excepto la representación del dinero como centavos largos.
jprete
Lo bueno es que la computadora entiende milicents.
tylermac
1
O además, ¿por qué usar decimales cuando podemos usar fracciones?
tylermac
66
@Scrooge: irónicamente, no puedes representar 0.6 en una carroza.
Martin Beckett

Respuestas:

28

Porque son, para la mayoría de los propósitos, más precisos que los enteros.

Ahora como es eso? "para la velocidad de un objeto en un juego ..." este es un buen ejemplo para tal caso. Digamos que necesita tener algunos objetos muy rápidos, como balas. Para poder describir su movimiento con variables de velocidad enteras, debe asegurarse de que las velocidades estén en el rango de las variables enteras, eso significa que no puede tener un ráster arbitrariamente fino.

Pero entonces, es posible que también desee describir algunos objetos muy lentos, como la manecilla de la hora de un reloj. Como esto es aproximadamente 6 órdenes de magnitud más lento que los objetos de bala, los primeros ld (10⁶) ≈ 20 bits son cero, lo que descarta los short inttipos desde el principio. Ok, hoy tenemos longs en todas partes, lo que nos deja con 12 bits aún cómodos. Pero incluso entonces, la velocidad del reloj será exacta a solo cuatro decimales. Ese no es un reloj muy bueno ... pero ciertamente está bien para un juego. Simplemente, no querrás hacer que la trama sea mucho más gruesa de lo que ya es.

... lo que genera problemas si algún día desea introducir un nuevo tipo de objeto aún más rápido. No queda "espacio libre".

¿Qué sucede si elegimos un tipo de flotador? Mismo tamaño de 32 bits, pero ahora tiene una precisión total de 24 bits para todos los objetos. Eso significa que el reloj tiene la precisión suficiente para mantenerse sincronizado al instante durante años. Las balas no tienen mayor precisión, pero solo "viven" por fracciones de segundo de todos modos, por lo que sería completamente inútil si lo tuvieran. Y no se mete en ningún tipo de problema si desea describir objetos aún más rápidos (¿por qué no la velocidad de la luz? No hay problema) o mucho más lentos. Ciertamente no necesitarás tales cosas en un juego, pero a veces sí las necesitas en simulaciones físicas.

Y con los números de coma flotante, obtienes esta misma precisión siempre y sin tener que elegir inteligentemente algún ráster no obvio. Ese es quizás el punto más importante, ya que tales necesidades de elección son muy propensas a errores.

a la izquierda
fuente
Los enteros son perfectamente precisos. La imprecisión depende del cálculo incorrecto.
fjdumont
15
Los enteros son perfectamente precisos solo cuando los usa para representar números enteros (ℤ). Representar cualquier otra cosa significa, de hecho, un cálculo incorrecto. En tal caso, tiene dos posibilidades: definir un tipo que se adapte perfectamente a los números que realmente desea representar. esto es posible, por ejemplo, Mathematica puede hacerlo. Pero es muy complicado y costoso en tiempo, y generalmente no vale la pena el esfuerzo porque en realidad no necesita una precisión perfecta. Pero necesita una buena precisión, y ahí es donde los flotadores generalmente hacen un mejor trabajo que los enteros.
Leftaroundabout
53

Los usa cuando describe un valor continuo en lugar de uno discreto . No es más complicado de describir que eso. Simplemente no cometa el error de asumir que cualquier valor con un punto decimal es continuo. Si cambia todo a la vez en trozos, como agregar un centavo, es discreto.

Karl Bielefeldt
fuente
28

Realmente tienes dos preguntas aquí.

¿Por qué alguien necesita matemática de punto flotante, de todos modos?

Como señala Karl Bielefeldt, los números de coma flotante le permiten modelar cantidades continuas, y las encuentra por todas partes, no solo en el mundo físico, sino incluso en lugares como negocios y finanzas.

He usado matemática de punto flotante en muchas, muchas áreas en mi carrera de programación: química, trabajando en AutoCAD e incluso escribiendo un simulador de Monte Carlo para hacer predicciones financieras. De hecho, hay un tipo llamado David E. Shaw que utilizó técnicas de modelado científico basadas en coma flotante en Wall Street para generar miles de millones.

Y, por supuesto, hay gráficos por computadora. Consulté sobre el desarrollo de los dulces visuales para las interfaces de usuario, y tratar de hacerlo hoy en día sin una sólida comprensión de coma flotante, trigonometría, cálculo y álgebra lineal, sería como presentarse a un tiroteo con una navaja.

¿Por qué alguien necesitaría un flotador frente a un doble ?

Con las representaciones estándar IEEE 754, un flotante de 32 bits le proporciona aproximadamente 7 dígitos decimales de precisión y exponentes en el rango de 10 -38 a 10 38 . Un doble de 64 bits le proporciona aproximadamente 15 dígitos decimales de precisión y exponentes en el rango de 10-307 a 10 307 .

Puede parecer que un flotador sería suficiente para lo que alguien razonablemente necesitaría, pero no lo es. Por ejemplo, muchas cantidades del mundo real se miden en más de 7 dígitos decimales.

Pero más sutilmente, hay un problema coloquialmente llamado "error de redondeo". Las representaciones de punto flotante binario solo son válidas para valores cuyas partes fraccionarias tienen un denominador que tiene una potencia de 2, como 1/2, 1/4, 3/4, etc. Para representar otras fracciones, como 1/10, "redondeas" el valor de la fracción binaria más cercana, pero está un poco mal: ese es el "error de redondeo". Luego, cuando haces cálculos matemáticos con esos números inexactos, las inexactitudes en los resultados pueden ser mucho peores de lo que comenzaste, a veces los porcentajes de error se multiplican o incluso se acumulan exponencialmente.

De todos modos, cuantos más dígitos binarios tenga que trabajar, más cercana será su representación binaria redondeada al número que está tratando de representar, por lo que su error de redondeo será menor. Luego, cuando hace cálculos matemáticos, si tiene muchos dígitos con los que trabajar, puede realizar muchas más operaciones antes de que el error de redondeo acumulado se acumule donde es un problema.

En realidad, los dobles de 64 bits con sus 15 dígitos decimales no son lo suficientemente buenos para muchas aplicaciones. Estaba usando números de punto flotante de 80 bits en 1985, y IEEE ahora define un tipo de punto flotante de 128 bits (16 bytes), para el cual puedo imaginar usos.

Bob Murphy
fuente
2
+1 Bob, mi experiencia con sistemas de control de alta resolución como el telescopio para astronomía es que los dobles de 64 bits no son lo suficientemente buenos a menos que ordene sus términos. Lo mismo para el control de incendios y la navegación de largo alcance
Tim Williscroft
20

Es una idea falsa común, que en todas partes donde se trata con dinero, debe almacenar su valor como entero (centavos). Si bien en algunos casos simples como la tienda en línea es cierto, si tiene algo más avanzado, no ayuda mucho.

Veamos un ejemplo: un desarrollador gana $ 100,000 al año. ¿Cuál es el salario exacto de su mes? Con el número entero obtienes el resultado $ 8333.33 (¢ 833333), que multiplicado por 12 es $ 99,999.96. ¿Mantenerlo como entero ayuda? No, no lo hizo.

¿Los bancos siempre usan valores decimales / enteros? Bueno, lo hacen por parte transaccional. Pero, por ejemplo, tan pronto como comience a hablar de banca de inversión, con la excepción de realizar un seguimiento de las transacciones reales, todo lo demás es flotante. Dado que todo es código interno, no lo verá, pero puede tomar un pico en QuantLib , que es esencialmente el mismo (excepto mucho más limpio ;-).

¿Por qué usar flotadores? Porque usar decimal no ayuda en absoluto cuando usas funciones como raíz cuadrada, logaritmos, potencias con exponentes no enteros, etc. Y, por supuesto, los flotadores son mucho más rápidos que los tipos decimales.

vartec
fuente
1
@ Job: los decimales y las carrozas son muy diferentes. Usted puede almacenar 0,1 exactamente en un tipo decimal, pero no en un flotador o doble.
Scott Whitlock
3
Tenía otra pregunta Si pagaste $100,000/12y usaste una carroza. ¿Por qué el resultado sería exactamente $ 100,000? ¿Por qué el flotador (o decimal) no se redondea hacia arriba o hacia abajo cada vez que alguien paga? Me refiero a la hora de escribir un cheque (no puedes hacer 1/2 o 1/3 de un centavo) o un depósito directo (supongo que tiene las mismas limitaciones)
@acid: >>> x = 100000 / 12.0 >>> x * 12
100000.0
releer mi comentario? mi pregunta es cuando uso el software para crear un cheque todos los meses. Como no se puede pagar 1/2 centavo, ¿cómo obtiene la persona el monto total después de un año?
2
@acid: no puede usar la división recta, independientemente de si usa entero, decimal o dividir como flotante y luego redondear. Ese es el punto, usar decimal no lo ayuda en ese caso.
vartec
4

Lo que ha descrito son soluciones perfectamente buenas para situaciones en las que controla todas las entradas y salidas .

En la palabra real no es el caso. Deberá poder hacer frente a los sistemas que le proporcionan sus datos como un valor real con cierto grado de precisión y esperará que devuelva los datos en el mismo formato. En tales casos , encontrará estos problemas.

De hecho, encontrará estos problemas incluso si utiliza los trucos que enumera. Al calcular el impuesto del 17.5% sobre un precio, obtendrá centavos fraccionales, ya sea que almacene el valor en dólares o centavos. Debe redondear correctamente ya que el recaudador de impuestos se molesta mucho si no le paga lo suficiente. Usar los moneytipos correctos (cualesquiera que sean en el idioma que está usando) lo salvará de un mundo de dolor.

ChrisF
fuente
¿Cuál es el tipo de dinero? (idioma o enlace de referencia) y ¿por qué es ese el tipo 'correcto'? ¿Es porque es ... 128bits o más algo? Mi otra ¿por qué usar mis 'trucos' sería incorrecto? Tienes un número entero por centavo. Si lo multiplica por .175 obtendrá un número entero y lo usará para lo que quiera. Pensando en su ejemplo, creo que float podría mantener mi valor con suficiente precisión, pero no tendré que preocuparme de que 0.3f == 0.3d sea falso. -editar- y +1
1
@ acidzombie24: no me refería a un tipo específico, sino a cualquier tipo que utilice su lenguaje para representar valores monetarios. Además, si tienes 10 centavos y multiplicas por 0.175 tienes 1.75 centavos, ¿cómo lidias con eso con la aritmética de enteros? ¿Es 1 centavo o 2 centavos? Si se equivoca, su cliente podría terminar siendo dueño del contribuyente con mucho dinero.
ChrisF
Nunca debe multiplicar 10 (un número entero) por .175 (un número real / flotante) porque no debe mezclar números exactos con números inexactos; El resultado será inexacto. En otras palabras, en un sistema de números exactos, un valor como .175 nunca existiría, por lo que este es un cálculo no sensorial. Una mejor solución es multiplicar 10000 por 175 e insertar manualmente un punto decimal cuando corresponda.
Barry Brown
8
@Barry - Lo sé. Estaba tratando de ilustrar el tipo de problema que tienes. También existe un valor como 0.175 si la tasa impositiva es 17.5% y necesita calcular el impuesto sobre un artículo que cuesta 10 centavos.
ChrisF
1
@acidzombie: El tipo correcto para usar por dinero es un decimal de punto fijo con alta precisión (al menos 4 puntos decimales). Sin dudas, quejas o peros. Almacenar valores monetarios como centavos no es suficiente, porque en la práctica solo le da dos puntos de precisión.
Aaronaught
3

"Dios creó los números enteros, todo lo demás es obra del hombre". - Leopold Kronecker (1886).

Por definición, no necesita ningún otro tipo de números. La integridad de Turing para un lenguaje de programación se basa en las relaciones simples entre los diversos tipos de números. Si puede trabajar con números enteros (a / k / a números naturales), puede hacer cualquier cosa.

La pregunta es un poco engañosa porque no los necesitas . ¿Quizás quieres lugares donde sea conveniente u óptimo o más barato o algo así?

S.Lott
fuente
77
También podemos prescindir de números enteros, ya que uno también puede construirlos usando solo operaciones de teoría de conjuntos y el conjunto vacío. Pero tanto eso como argumentar desde la integridad de Turing son reduccionismo académico llevado al extremo.
Bob Murphy
44
Además, la integridad de Turing solo se aplica a la informática. Ni los números enteros ni los racionales son matemáticamente completos, ya que ninguno está cerrado a la convergencia de secuencias de Cauchy. Entonces Kronecker estaba lleno de aire caliente: si desea un espacio métrico completo que incluya los números enteros, debe ser real: xkcd.com/849
Bob Murphy
1
@Bob Murphy: "reduccionismo académico llevado al extremo". Precisamente. La pregunta es pobre y lleva a esto como posible respuesta.
S.Lott
2

En una oración, los tipos decimales de coma flotante encapsulan la conversión hacia y desde valores enteros (que es todo lo que la computadora sabe cómo tratar en el nivel binario; no hay punto decimal en binario) proporcionando una lógica, generalmente fácil de entender. Comprender la interfaz para los cálculos de números decimales.

Francamente, decir que no necesitas flotadores porque sabes cómo hacer cálculos decimales con números enteros es como decir que sabes hacer aritmética a mano, entonces, ¿por qué usar una calculadora? Entonces conoces el concepto; Bravo. No significa que tenga que ejercer ese conocimiento todo el tiempo. A menudo es más rápido, más barato y más comprensible para un genio no binario decir simplemente 3.5 + 4.6 = 8.1 en lugar de convertir los higos a una cantidad entera.

KeithS
fuente
1

La principal ventaja de los tipos de punto flotante es que, desde una perspectiva de tiempo de ejecución, dos o tres formatos (deseo que más idiomas admitan formatos de 80 bits) serán suficientes para la mayoría rápida de los propósitos computacionales. Si los lenguajes de programación pudieran admitir fácilmente una familia de tipos de punto fijo, la complejidad del hardware requerida para un nivel de rendimiento dado a menudo sería menor con los tipos de punto fijo que con el punto flotante. Desafortunadamente, proporcionar tal soporte está lejos de ser "fácil".

Para que un lenguaje de programación satisfaga eficientemente el 98% de las necesidades numéricas de las aplicaciones, tendría que incluir docenas de tipos y proporcionar operaciones definidas para lo que pueden ser cientos de combinaciones; Además, incluso si un lenguaje de programación tuviera un maravilloso soporte de punto fijo, algunas aplicaciones aún tendrían que mantener una precisión relativa aproximadamente constante en un rango lo suficientemente grande como para requerir punto flotante. Dado que las matemáticas de punto flotante serán necesarias en algunas ocasiones en cualquier caso, hacer que los proveedores de hardware se centren en el rendimiento matemático con dos o tres formatos de punto flotante y que el código use esos formatos siempre que funcionen razonablemente bien, generalmente logrará mejores resultados. "aprovechar el dinero" de lo que trataría de optimizar el comportamiento de las matemáticas de punto fijo.

Por cierto, las matemáticas de punto fijo fueron más ventajosas con los procesadores de 8 y 16 bits que con los de 32 bits. En un procesador de 8 bits, en una situación en la que 32 bits no serían suficientes, un tipo de 40 bits solo costaría un 25% más de espacio y un 25-50% más de tiempo que el tipo de 32 bits, y requeriría un 37,5% menos espacio y 37.5-60% menos tiempo que un tipo de 64 bits. En una plataforma de 32 bits, si un tipo de 32 bits no es suficiente para algo, a menudo hay pocas razones para usar algo de menos de 64 bits. Si un tipo de punto fijo de 48 bits fuera adecuado, un "doble" de 64 bits funcionará tan bien como el tipo de punto fijo.

Super gato
fuente
0

En general, debe tener mucho cuidado al usarlos. Comprender la pérdida de precisión que puede surgir incluso de cálculos simples es un desafío. Por ejemplo, promediar una lista de números como esta es una muy mala idea:

double average(List<Double> data) {
  double ans = 0;
  for(Double d : data) {
    ans += d;
  }
  return ans / data.size();
}

La razón es que, para listas lo suficientemente grandes, básicamente pierde todos los puntos de datos cuando anses lo suficientemente grande (ver, por ejemplo, esto ). El problema con este código es que para listas pequeñas, probablemente solo funcionará, solo se rompe a escala.

Personalmente, creo que solo debe usarlos cuando: a) el cálculo realmente debe ser rápido; b) no le importa que el resultado sea muy diferente (a menos que realmente sepa lo que está haciendo).

redjamjar
fuente
-1

Un pensamiento es que usaría las representaciones flotantes o dobles cuando necesite tratar con valores fuera del rango entero.

Las arquitecturas actuales (aproximadamente) tienen un rango entero con signo de +/- 2.147.483.647 (32 bits) o +/- 9.223.372.036.854.775.807 (64 bits). Unsigned extiende eso por un factor de 2.

Los flotadores IEEE 754 (aproximadamente) van desde +/- 1.4 × 10 ^ −45 a 3.4 × 10 ^ 38. Double extiende ese rango a +/- 5 × 10−324 ± 2.225 × 10 ^ −308 con muchas condiciones y detalles omitidos aquí.

Por supuesto, la razón más asombrosamente obvia es que es posible que deba representar -0 ;-)

Stephen
fuente
Números principalmente de artículos de Wikipedia y están destinados a ser ilustrativos. Excepto -0, eso es solo por diversión.
Stephen
El problema es que hay MUCHOS enteros en ese enorme rango que no están representados en absoluto.
Barry Brown
@BarryBrown Absolutamente correcto. "muchas condiciones y detalles omitidos" sin embargo.
Stephen
-1

La razón habitual es porque son rápidos, ya que la JVM generalmente usa soporte de hardware subyacente (a menos que use estrictamente fp).

Vea /programming/517915/when-to-use-strictfp-keyword-in-java para lo que implicatricfp.

Comunidad
fuente
La matemática de punto flotante es más rápida que la matemática entera ¿En qué procesador los cálculos de coma flotante toman menos ciclos que los enteros?
this.josh
1
@ this.josh, depende en gran medida del número de dígitos que tenga en sus números. Además, los enteros no pueden dividir con precisión lo que puede o no ser importante.
-2

Es por eso que necesitamos sistemas operativos de 256 bits.

La longitud del tablón (la distancia más pequeña que puede medir) = 10 ^ -35m
El universo observable es 14Bn parsecs de ancho = 10 ^ 25m
Para que pueda medir cualquier cosa en unidades de la longitud del tablón como enteros si tiene solo 200 bits de precisión.

Martin Beckett
fuente
2
-1: ¿y si estás simulando cosas en una escala mayor que el universo observable?
amara el
2
@sparkleshy, ¡para eso están los punteros FAR!
Martin Beckett