Al González
30-03-2012, 20:48:27
Hola, para compartirles esto y de paso indagar más.
Me topé con cierto error de cálculo matemático en algo que estoy programando. Cuando el resultado tiene que ser 50, me arroja 5, cuando tiene que ser 800, me arroja 8, pero si tiene que ser 51 u 802, entonces sí lo respeta:
Debe ser Resulta
50 5 (incorrecto)
800 8 (incorrecto)
51 51 (correcto)
802 802 (correcto)
En pocas palabras, elimina los ceros significativos a la derecha del número. La operación matemática que realizo es por demás simple de entender:
X = (A * B) / B
Siendo A el número 50, 800, 51, 802, etcétera, y B una cantidad indeterminada. Si Multiplico A por B y al resultado le hago la operación inversa (dividirlo entre B), el resultado final será nuevamente en A, ¿cierto? Pero no sucede así en la versión 7 de Delphi cuando:
A es un entero que termina en 0 de tipo Currency,
la primera aparición de B es de tipo variante BCD,
la segunda aparición de B es de tipo Currency,
y toda la operación se hace en la misma sentencia
Es decir: X := (A_Currency * B_VariantBCD) / B_Currency
Lo he probado en Delphi 2007 también, con la fortuna de que ahí no ocurre el error de cálculo, por lo cual creo que se trata de un defecto corregido en alguna de las versiones posteriores a la 7.
Les agradecería si me ayudan ha probar este sencillo código con la versión de la que ustedes dispongan:
Uses
FmtBCD;
procedure TForm1.Button1Click(Sender: TObject);
Var
C1, C2, C3 :Currency;
V :Variant;
begin
C1 := 50;
C2 := 149.88;
V := VarFMTBcdCreate (149.88);
// (50 * 149.88) / 149.88 = 50
C3 := (C1 * V) / C2; // ¡Resulta 5!
ShowMessage (CurrToStr (C3));
// Una forma de evitar el problema es usar molde de tipo sobre el Variant
C3 := (C1 * Currency (V)) / C2; // Correcto, resulta 50
ShowMessage (CurrToStr (C3));
// Como la primera prueba pero con 51 en lugar de 50
C1 := 51;
C3 := (C1 * V) / C2; // Resulta 51, como debe ser
ShowMessage (CurrToStr (C3));
// Como la primera prueba pero con 800
C1 := 800;
C3 := (C1 * V) / C2; // ¡Resulta 8!
ShowMessage (CurrToStr (C3));
// Como la primera prueba pero con 802
C1 := 802;
C3 := (C1 * V) / C2; // Resulta 802, como debe ser
ShowMessage (CurrToStr (C3));
end;
Desconozco si este problema fue reportado en su momento, ya que no tuve suerte de encontrar información sobre ello. Pero puede que esta discusión (http://delphigroups.info/2/02/254388.html) del año 1902 (con razón dicen por ahí que Delphi está viejo :D) tenga alguna relación.
Algunos usuarios de versiones anteriores a la corrección (por lo menos desde la aparición de la unidad FmtBCD hasta la 7) se han de sentir despreocupados por esto al ver que el variante es obtenido con la función VarFmtBCDCreate, pues quizá no la usan. Pero ojo avizor, porque ese mismo tipo de variante es el que devuelve un típico campo monetario Numeric que en Delphi suele representarlo un objeto de clase TFmtBCDField. La recomendación entonces creo que sería usar siempre su propiedad AsCurrency en las versiones de Delphi que presenten ese problema. Además de la 7, ¿cuál otra?
Para mi caso actual implicaría cambiar muchas referencias tipo "ConjuntoDeDatos ['Campo']" por "ConjuntoDeDatos.FieldByName ('Campo').AsCurrency", pero creo lo mejor va a ser derivar una clase interpuesta de TFmtBCDField y redefinir su método virtual GetAsVariant, de manera que éste devuelva un variante Currency en lugar de un variante BCD. Cuando suba el proyecto a Delphi 2010 o XE2 (que les tengo tantas ganas como escasez de dinero) desecharé la clase interpuesta.
Un abrazo sin-cero.
Al González. :)
Me topé con cierto error de cálculo matemático en algo que estoy programando. Cuando el resultado tiene que ser 50, me arroja 5, cuando tiene que ser 800, me arroja 8, pero si tiene que ser 51 u 802, entonces sí lo respeta:
Debe ser Resulta
50 5 (incorrecto)
800 8 (incorrecto)
51 51 (correcto)
802 802 (correcto)
En pocas palabras, elimina los ceros significativos a la derecha del número. La operación matemática que realizo es por demás simple de entender:
X = (A * B) / B
Siendo A el número 50, 800, 51, 802, etcétera, y B una cantidad indeterminada. Si Multiplico A por B y al resultado le hago la operación inversa (dividirlo entre B), el resultado final será nuevamente en A, ¿cierto? Pero no sucede así en la versión 7 de Delphi cuando:
A es un entero que termina en 0 de tipo Currency,
la primera aparición de B es de tipo variante BCD,
la segunda aparición de B es de tipo Currency,
y toda la operación se hace en la misma sentencia
Es decir: X := (A_Currency * B_VariantBCD) / B_Currency
Lo he probado en Delphi 2007 también, con la fortuna de que ahí no ocurre el error de cálculo, por lo cual creo que se trata de un defecto corregido en alguna de las versiones posteriores a la 7.
Les agradecería si me ayudan ha probar este sencillo código con la versión de la que ustedes dispongan:
Uses
FmtBCD;
procedure TForm1.Button1Click(Sender: TObject);
Var
C1, C2, C3 :Currency;
V :Variant;
begin
C1 := 50;
C2 := 149.88;
V := VarFMTBcdCreate (149.88);
// (50 * 149.88) / 149.88 = 50
C3 := (C1 * V) / C2; // ¡Resulta 5!
ShowMessage (CurrToStr (C3));
// Una forma de evitar el problema es usar molde de tipo sobre el Variant
C3 := (C1 * Currency (V)) / C2; // Correcto, resulta 50
ShowMessage (CurrToStr (C3));
// Como la primera prueba pero con 51 en lugar de 50
C1 := 51;
C3 := (C1 * V) / C2; // Resulta 51, como debe ser
ShowMessage (CurrToStr (C3));
// Como la primera prueba pero con 800
C1 := 800;
C3 := (C1 * V) / C2; // ¡Resulta 8!
ShowMessage (CurrToStr (C3));
// Como la primera prueba pero con 802
C1 := 802;
C3 := (C1 * V) / C2; // Resulta 802, como debe ser
ShowMessage (CurrToStr (C3));
end;
Desconozco si este problema fue reportado en su momento, ya que no tuve suerte de encontrar información sobre ello. Pero puede que esta discusión (http://delphigroups.info/2/02/254388.html) del año 1902 (con razón dicen por ahí que Delphi está viejo :D) tenga alguna relación.
Algunos usuarios de versiones anteriores a la corrección (por lo menos desde la aparición de la unidad FmtBCD hasta la 7) se han de sentir despreocupados por esto al ver que el variante es obtenido con la función VarFmtBCDCreate, pues quizá no la usan. Pero ojo avizor, porque ese mismo tipo de variante es el que devuelve un típico campo monetario Numeric que en Delphi suele representarlo un objeto de clase TFmtBCDField. La recomendación entonces creo que sería usar siempre su propiedad AsCurrency en las versiones de Delphi que presenten ese problema. Además de la 7, ¿cuál otra?
Para mi caso actual implicaría cambiar muchas referencias tipo "ConjuntoDeDatos ['Campo']" por "ConjuntoDeDatos.FieldByName ('Campo').AsCurrency", pero creo lo mejor va a ser derivar una clase interpuesta de TFmtBCDField y redefinir su método virtual GetAsVariant, de manera que éste devuelva un variante Currency en lugar de un variante BCD. Cuando suba el proyecto a Delphi 2010 o XE2 (que les tengo tantas ganas como escasez de dinero) desecharé la clase interpuesta.
Un abrazo sin-cero.
Al González. :)