Ir al contenido


Foto

problemas valores double


  • Por favor identifícate para responder
9 respuestas en este tema

#1 razadi

razadi

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 681 mensajes
  • LocationMéxico D.F.

Escrito 07 junio 2012 - 07:03

Saludos amigos.

Tengo un problema, a ver si alguien le ha sucedido y ver como lo ha resuelto:

tengo un zquery que al barrerlo recupero ciertos datos d ela siguiente manera:



delphi
  1. ...
  2. var
  3.   dSal,dTof,dCob,dAnt: double;
  4. begin
  5. ...
  6.   while not qry.Eof do begin
  7.       ...
  8.       dTof := FieldByName('CXC_TOF').AsFloat;
  9.       dAnt := FieldByName('CXC_ANT').AsFloat;
  10.       dCob := FieldByName('CXC_COB').AsFloat;
  11.  
  12.       dSal := dTof-dAnt-dCob;
  13.       next;
  14.   end;
  15. ...
  16. end;



cuando me da los siguiente valores:



delphi
  1. dTof := 632174.07;
  2. dAnt := 606751;
  3. dCob := 25423.07;



En teoria, con la operación anterior y esos valores obtenidos del query como lo expongo, es comun pensar que el valor de dSal := 0;

y pues nop

el valor que me da es el siguiente:

dSal := -5.0931703299e-11

ahhhhhh!!!!!

alguien sabra por que me hace esto? 8o| 8o| 8o| 8o| 8o| 8o| 8o|

Saludos de antemano y gracias... (b)

  • 0

#2 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.301 mensajes
  • LocationArgentina

Escrito 07 junio 2012 - 08:31

Hola Razadi,
¿Estás teniendo en cuenta de que, al menos de esa muestra de código (y obviando lo que se haga en esos puntos suspensivos), cada vez que pasas al nuevo registro se pierden los valores leídos anteriormente en tus variables dXXX? Es posible parte del problema esté allí y realmente el valor que tu crees que es no lo es y tengas algun valor "basura" en algún lado.

Recuerda además que cuando se trata de coma flotante los valores no son exactos sino aproximados, aún cuando internamente la FPU esté equipada con un aritmética de redondeo exacto, y SIEMPRE tendrás un valor exacto en el orden de +- 0.5 ulp del que obtienes.
De allí que se sugiere tratar con márgen de tolerancia como el error relativo y/o absoluto. Delphi cuenta con un juego de funciones para tal efecto, entre ellas IsZero()  ;)

Para saber más sobre como el problema habría que conocer como es que internamente trata Zeos a los campos. ¿Que clase de TField genera para el tipo de datos de dichos campos? ¿De que tipo de datos es el campo de la base de datos?
Yo desconozco Zeos.

Como consejo: si esos campos hacen referencia a valores monetarios sugiero cambiar el tipo de datos por uno de punto fijo, Currency es el más aconsejado. Y por lo general basta con establecer en true la propiedad AsCurrency para que todo se solucione; aunque es posible que tu ya hayas considerado esta posibilidad. En la base de datos almacenarlos se recomienda como NUMERIC() o DECIMAL().
A menos a que efectivamente requieras trabajar con aritemética de punto flotante, mejor emplea la aritemética de punto fijo y establece la precisión y cantidad de decimales con la que vas a trabajar... te hará más fácil la vida.

Antes hemos discutido sobre este tema y se han propuesto alternativas, consejos. Puedes tomarlo como referencia.

Saludos,


  • 0

#3 razadi

razadi

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 681 mensajes
  • LocationMéxico D.F.

Escrito 07 junio 2012 - 09:00

claro delphius antes que nada gracias por contestar, si mira en la base que por cierto es firebird estan como numeric(15,2) por eso los tomo como asfloat y si la variable dSal antes de cada vuelta la establezco en cero antes de cada operacion ppr eso se me hace muy raro ese error.
  • 0

#4 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.301 mensajes
  • LocationArgentina

Escrito 07 junio 2012 - 09:17

Ummm, y ¿que tipo de Field te genera Zeos para dichos campos? ¿Tienes la propiedad Currency en true? Y de paso que versión de Delphi.
Desde que Al se topó con un pequeño bug en Delphi con los TFMTBCDFloat, el uso de Currency en D7 (y anterior, al menos) y que resulta ser por cierta incongruencia entre algunas conversiones desde Variants, Double, Currency y los TFMTBCD ya hasta dudo de cualquier cosa.

Cuanto más nos puedas decir al respecto más fácil será poder encontrar donde está el problema y ver como encararlo.

Saludos,
  • 0

#5 razadi

razadi

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 681 mensajes
  • LocationMéxico D.F.

Escrito 08 junio 2012 - 07:06

Si mira actualmente estoy con delphi 2007, y lo toma como Float, lo curioso es que antes no me había pasado esto, apenas ayer que tengo que comparar el valor de dSal y ahi es donde me di cuenta.
  • 0

#6 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 08 junio 2012 - 07:07

Se trata de un viejo BUG que solo fue resuelto hace muy poco. http://qc.embarcader...in.aspx?d=87092.

Yo tuve este problema, puesto que uso el TCxGrid de DevExpress, que lo que hace es crear arrays en memoria con base en el dataset, y un campo declarado en Firebird como numeric, es tratado por la mayoría de componentes de conexión como TFMTBCDField, en consecuencia los resultados de las sumas que generaba el TCXGrid eran muchas veces erróneas (pues lo que hacía era recorrer el array  de dicho tipo para sumarlo). La solución temporal a eso era crear un campo calculado de tipo Currency o Float que tomara el valor del campo original y ya sobre el campo calculado hacer las respectivas operaciones.

Saludos
  • 0

#7 razadi

razadi

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 681 mensajes
  • LocationMéxico D.F.

Escrito 08 junio 2012 - 07:09

Ok Wilso lo tomare en cuenta, gracias.
  • 0

#8 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.092 mensajes
  • LocationMurcia, España

Escrito 08 junio 2012 - 09:19

De todas formas no es algo extraño que una resta no te exactamente cero en los float, siempre puedes tener decimales "ocultos" en la posición 12 que no veas al imprimirlos en un reporte (ya que se redondean a 2 decimales, por ejemplo).

Yo uso float en estos campos, pero tras cada operación realizada sobre una cantidad de dinero, siempre la redondeo a la precisión de la moneda, y así solo manejo valores "posibles" de dinero (lo pongo en el trigger before udpate si no quiero preocuparme de esas cosas).

En FB tengo una funcion pequeña que redondea a N decimales, 2 en mi caso, y la idea es muy simple:

Cantidad:= trunc(Cantidad*10^N)/10^N

Si N es dos, 10^N es 100, por lo que si le pasas 12.0234 lo convierte primero en 1202.34, luego le quita los decimales y obtiene 1202 y esto lo divide por 100, obteniendo 12.02

Otra opción mejor en FB es declararlos como numeric(10,2), así todo esto se hace internamente en FB de forma automática (en ese caso el redondeo es al entero más próximo, por lo que 12.007 se redondea a 12.01, al contrario que trunc que te daría 12.00).
  • 0

#9 razadi

razadi

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 681 mensajes
  • LocationMéxico D.F.

Escrito 08 junio 2012 - 11:23

Gracias amigos, de hecho lo tengo en FB y esta delcarado de la siguiente manera:


CXC_TOF NUMERIC(15, 2),


Y los valores los reviso directamente en Memoria, por eso se me hacia raro, pero bueno ya quedo "al parecer", lo único que hice es en vez de declarar las variables como double las declare como Currency y listo ya al hacer la operación ya me sale 0.

Bueno que quede aqui acentado que con los float de Zeos se present unos errorcillos y para asegurar mejor hay que declarar las variables como currency para estas operacións de dinero.

Saludos y gracias. (b) (b) (b) (b) (b)


  • 0

#10 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.301 mensajes
  • LocationArgentina

Escrito 08 junio 2012 - 08:54

Hola Razadi,

Me alegro de que te haya sido de ayuda  ;) Precisamente por ello yo te comentaba al inicio que si esos números eran de carácter monetario lo mejor es emplear Currency. Por seguridad se debe establecer en true la propiedad Currency de los campos, emplear variables Currency, trabajar en lo posible con una cantidad homogénea de decimales y evitar mezclar variables de diferentes tipos, y utilizar la propiedad AsCurrency* para pasar y leer los datos.

* Aunque como he citado en un post antes, con precaución en D7- que hay un pequeño bug cuando se utiliza conversiones de Variants a Currency y se está empleando componentes de la clase TFMTBCDField.

Saludos,
  • 0




IP.Board spam blocked by CleanTalk.