Hace 5 años | Por Sanngetall a microsiervos.com
Publicado hace 5 años por Sanngetall a microsiervos.com

Desde el departamento de Maravillas de los cálculos en coma flotante nos llega esta sencilla y curiosa pregunta: ¿Qué resultado da como respuesta tu lenguaje de programación favorito a este cálculo? 9999999999999999,0 - 9999999999999998,0 = ? Las respuestas posibles van desde el 1 correcto en Wolfram Alpha, Perl6 y Soup a valores como 2,0, 0,0, el rotundo 0 de Google o el impreciso 2,000000 de C o Go. Así es la dura vida de la precisión en coma flotante.

Comentarios

S

#5 Podría ser peor. Estoy haciendo un proyecto en arduino y una de las funciones es que pite cada vez que se da un evento. Ya me rompí los cuernos con lo Timers, pero es que encima el otro día pienso

- Joer parece que suena un poco flojo, voy a cambiar la resistencia del zumbador por una más baja

Y al hacerlo cada vez que se producia un sonido pitaba tres veces y me dejaba el sistema colgado un par de segundos. Suerte que "acerté" con la resistencia de primeras, porque si no me hubiera vuelto loco

T

#7 ya, si en peores plazas hemos toreado.

Pero es divertido y te abre la mente a nuevas experiencias.

Urasandi

#5 Me paso lo mismo con variables reales en TurboPascal hace 30 años.

squanchy

#5 Me has recordado un programa que hice en pascal-fc donde un ascensor subía y bajaba recogiendo pasajeros. Un proceso generaba habitantes que realizaban llamadas desde un piso al azar, queriendo subir o bajar a otro al azar. Con menos de 34 peticiones, el ascensor funcionaba de maravilla. Con 35 o más, se volvía loco. El profesor no fue capaz de encontrar el error en mi código. ¡Y es que no lo había!
En algún proceso interno de pascal-fc, había un desbordamiento de pila y en lugar de avisar con un mensaje de error, seguía para adelante tan pancho, pero ya con la memoria corrupta. Las ñapas de Harvard no son tan cojonudas como las españolas.

T

#18 me has recordado otro caso mío. En esa ocasión digamos que fue una práctica "open source", en plan que principalmente la escribieron unos, con ayuda pero ellos, y otros dos grupos (eran en grupos de dos) presentamos la misma práctica pero con adaptaciones más que nada cosméticas.

En este caso era en C y se trataba de simular la gestión de una cola de procesos por round-robin con el número de procesos y el "quantum" (o como se llamase) como variables. Los autores principales fueron, la defendieron y en paz. Mi apañero y yo decidimos ir dos días después a defenderla, por un lado para acabar de entender bien todo el código, en parte para hacerle pruebas. Era una cagada de mosca pero yo de C iba justito.

Y llegó el problema. Resulta que si el quantum era de 3 o más, no había problema, funcionaba perfectamente, daba la suma total de tiempo que tenía que dar, que era la suma de tiempos de cada proceso. Ahora, si el valor era 2, por algún extraño motivo que se nos escapaba, el resultado total era el real más uno. Daba igual los ejemplos que pusiésemos. Si tenía que dar 15, daba 16, si tenía que dar 50, daba 51. Pero más divertido era el valor 1. En ese caso el programa entraba en bucle y no acababa nunca. Y la ejecución era paso por paso, es decir, a cada ciclo de "procesador" había que darle a la barra espaciadora para que siguiese. Ya podías estar dándole hasta el día del juicio final.

Nos pasamos día y medio revisándola y no hubo narices a encontrar dónde estaba el fallo. Así que de perdidos al río. En la defensa nos preguntó lo que quiso y más sobre el código, del que salimos bien parados, y luego nos manda ejecutarlo. Y nos dice "ponedle de quantum 2". Gotita de sudor en la frente y pensamiento de "la cagamos Luís...".

Además el tío va y se pone a anotar cómo va el programa paso por paso, anotando los valores uno por uno a cada ciclo. Yo ya buscando la excusa para el error que iba a dar. Pero el tío va y suelta "dale ya hasta el final". Creo que no le di a una barra espaciadora más rápido en la vida.

El tío mira el resultado y dice "...29, ¿es eso lo que tiene que dar?". Nosotros "sí, claro". "Vale, os podéis ir".

Lo que todavía hoy, un puñado de lustros después, aún no sé, es cómo a los otros dos grupos con esencialmente el mismo código (ya te digo, cambios cosméticos) no les ocurrió el error. O no lo probaron, y me cuesta creerlo, o ni idea.

squanchy

#26 Tuve una historia similar con una práctica de laboratorio de tecnología de computadores. El profesor diciendo "y ya para acabar, la prueba de fuego", y nosotros con la gota de sudor en la sien porque sabíamos que aquel circuito no iba fino.

yuip

En matlab da 2 (reales de 8 bytes), en Fortran con reales de 8 bytes 2, y en Fortran con reales de 16 bytes da 1.

squanchy

#2 En la consola de chrome da 0, y en la de firefox igual.

B

En R da 2
> 9999999999999999.0 - 9999999999999998.0
[1] 2

Me ha pillado la lectura del post con la consola abierta.

a

Mejor usar precisión arbitraria con bc
Un ejemplito:

scale= 2000
sqrt(2)
1.414213562373095048801688724209698078569671875376948073176679737990
73247846210703885038753432764157273501384623091229702492483605585073
72126441214970999358314132226659275055927557999505011527820605714701
09559971605970274534596862014728517418640889198609552329230484308714
32145083976260362799525140798968725339654633180882964062061525835239
50547457502877599617298355752203375318570113543746034084988471603868
99970699004815030544027790316454247823068492936918621580578463111596
66871301301561856898723723528850926486124949771542183342042856860601
46824720771435854874155657069677653720226485447015858801620758474922
65722600208558446652145839889394437092659180031138824646815708263010
05948587040031864803421948972782906410450726368813137398552561173220
40245091227700226941127573627280495738108967504018369868368450725799
36472906076299694138047565482372899718032680247442062926912485905218
10044598421505911202494413417285314781058036033710773091828693147101
71111683916581726889419758716582152128229518488472089694633862891562
88276595263514054226765323969461751129160240871551013515045538128756
00526314680171274026539694702403005174953188629256313851881634780015
69369176881852378684052287837629389214300655869568685964595155501644
72450983689603688732311438941557665104088391429233811320605243362948
53170499157717562285497414389991880217624309652065642118273167262575
39594717255934637238632261482742622208671155839599926521176252698917
54098815934864008345708518147223181420407042650905653233339843645786
57967965192672923998753666172159825788602633636178274959942194037777
53681426217738799194551397231274066898329989895386728822856378697749
66251996658352577619893932284534473569479496295216889148549253890475
58288345260965240965428893945386466257449275563819644103169798330618
52019379384940057156333720548068540575867999670121372239475821426306
58513221740883238294728761739364746783743196000159218880734785761725
22118674904249773669292073110963697216089337086611567345853348332952
546758516447107578486024636008

D

¿Ahora han descubierto la representación binaria de los números decimales? Esto se sabe desde los inicios de la programación, si quieres decimales exactos usa las herramientas que los lenguajes dan para ello en lugar de operaciones en coma flotante, ejemplo que siempre sale y que los programadores noveles siempre se suelen equivocar es los cálculos con monedas, para que esos cálculos, o el de la noticia, te den el resultado esperado por un humano tienes cosas como Decimal en Python o BigDecimal en Java, en lugar de usar directamente la representación binaria que te da por defecto el lenguaje, o como se hacía antiguamente, guardalo en entero y luego ya pondrás la coma donde toque para pintarlo.

s

El Maxima con el Lisp y wxmaxima me da 2
El Lazarus con ObjetPascal me da 2
El Gambas3.8.4 basic me da 0


Lazarus código
----------
unit Unit1;



interface

uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;

type



TForm1 = class(TForm)
StaticText1: TStaticText;
ToggleBox1: TToggleBox;
procedure ToggleBox1Click(Sender: TObject);
private

public

end;

var
Form1: TForm1;

implementation





procedure TForm1.ToggleBox1Click(Sender: TObject);
var
a1: real;
a2: real;
result: real;

begin
a1:=9999999999999999.1;
a2:=9999999999999998.1;
result:= a1 - a2;

writeln(result);
StaticText1.Caption:= FloatToStr(result);
end;

end.


=======================================

Gambas código:
--------------
Public Sub Main()

Dim a1 As Float
Dim a2 As Float
Dim result As Float

a1 = 9999999999999999.1
a2 = 9999999999999998.1
result = a1 - a2

Print result

End



squanchy

#6 ¿No puedes restar los dos números sin necesidad de meterlos en variables? Declarar variables innecesarias es de parguelas.

s

#19 La costumbre para hacer más pruebas.. jejeje
Y porque pueden variar resultados según variables declaradas a ver que pasaba...

nah

squanchy

#20 Es que al declarar la variable, ya estás asignando tú un tipo, y ese tipo tiene una precisión concreta. Mientras que si pones el número directamente en la operación, es el compilador quien decide de qué tipo es esa constante. Ese es el problema que le veo a tu código.

s

#22
**
#20 Es que al declarar la variable, ya estás asignando tú un tipo, y ese tipo tiene una precisión concreta.
**
Exactamente

**
n, es el compilador quien decide de qué tipo es esa constant
*+
Exacto
Lo he puesto para "jugar" con todo ello

x

4 maravedíes y dos dinares

f

En el artículo se indica que el resultado de C (2,000000) es impreciso, cuando lo que debería decir es que es inexacto. Jodó con el bloguero....

squanchy

#4 El bloguero tiene razón. Se trata de un problema de precisión, más que de de exactitud. ¿Pi es 3.1416? Pues depende de la precisión que yo necesite para dar por correcto mi cálculo.
En el artículo, se da por hecho que espera una precisión hasta las unidades, por eso dice que es impreciso. Si para dar el resultado por bueno bastase una precisión hasta las decenas o las centenas, el resultado arrojado sería correcto.
En el caso que muestra, además de impreciso, es inexacto, pero ese es otro cantar.

f

#16 La exactitud es la diferencia entre el valor medido y la realidad, mientras que la precisión es el grado de coincidencia entre resultados independientes de una misma medida. Entonces, a no ser que el procesador en que han hecho el experimento tenga un bug, todos los resultados de para cualquiera de los experimentos van a coincidir, por lo que todos son igualmente precisos, mientras que aquellos que dan otro resultado que 1.0 son inexactos.

D

Los lenguajes de programación no usan comas si no puntos. Quien ha sido el PKT que ha escrito esta pregunta?

S

Respuestas de varios lenguajes
http://geocar.sdf1.org/numbers.html

minardo

Es curioso, en el artículo dicen que con java (no especifican qué version de la máquina virtual) da 2, pero acabo de hacer la prueba con groovy, lenguaje dinámico que corre sobre la jvm, y el resultado es 1.0

D

En python3 me da 2

ur_quan_master

Float o double?

yuip

#3 con double da resultado erroneo. Necesitas más precisión

D

Yo con lo último que programo es AutoHotKey y el resultado de:

MsgBox % 9999999999999999.0 - 9999999999999998.0

es 0, da igual si asigno los valores a una variable.

M

Todos los que programamis hemos padecido problemas de coma flotante.