Pregunta

AFAIK, el tipo de moneda en Delphi Win32 depende de la precisión del punto flotante del procesador. Debido a esto, tengo problemas de redondeo al comparar dos valores de Moneda, devolviendo resultados diferentes dependiendo de la máquina.

Por ahora estoy usando la función SameValue pasando un parámetro Epsilon = 0.009, porque solo necesito una precisión de 2 dígitos decimales.

¿Hay alguna forma mejor de evitar este problema?

¿Fue útil?

Solución

El tipo de moneda en Delphi es un entero de 64 bits escalado en 1 / 10,000; en otras palabras, su incremento más pequeño es equivalente a 0.0001. No es susceptible a problemas de precisión de la misma manera que lo es el código de coma flotante.

Sin embargo, si está multiplicando sus números de Moneda por tipos de punto flotante, o dividiendo sus valores de Moneda, el redondeo debe resolverse de una u otra forma. La FPU controla este mecanismo (se llama " palabra de control "). La unidad matemática contiene algunos procedimientos que controlan este mecanismo: SetRoundMode en particular. Puedes ver los efectos en este programa:

{$APPTYPE CONSOLE}

uses Math;

var
  x: Currency;
  y: Currency;
begin
  SetRoundMode(rmTruncate);
  x := 1;
  x := x / 6;
  SetRoundMode(rmNearest);
  y := 1;
  y := y / 6;
  Writeln(x = y); // false
  Writeln(x - y); // 0.0001; i.e. 0.1666 vs 0.1667
end.

Es posible que una biblioteca de terceros que esté utilizando establezca la palabra de control en un valor diferente. Es posible que desee establecer la palabra de control (es decir, el modo de redondeo) explícitamente en el punto de inicio de sus cálculos importantes.

Además, si sus cálculos alguna vez se transfieren a un punto flotante llano y luego a la Moneda, todas las apuestas están apagadas, demasiado difícil de auditar. Asegúrese de que todos sus cálculos estén en Moneda.

Otros consejos

No, la moneda no es un tipo de punto flotante. Es un decimal de precisión fija, implementado con almacenamiento de enteros. Puede compararse exactamente y no tiene los problemas de redondeo de, por ejemplo, Double. Por lo tanto, si está viendo valores inexactos en sus variables de Moneda, el problema no es el tipo de Moneda en sí, sino lo que está ingresando. Lo más probable es que tenga un cálculo de punto flotante en otra parte de su código. Ya que no muestra ese código, es difícil ser de más ayuda en esta pregunta. Pero la solución, en términos generales, será redondear los números de punto flotante a la precisión correcta antes de almacenar en la variable Moneda, en lugar de hacer una comparación inexacta en las variables Moneda.

La

forma más rápida y segura de comparar dos valores de currency es ciertamente asignar las variables a su representación interna de Int64 :

function CompCurrency(var A,B: currency): Int64;
var A64: Int64 absolute A;
    B64: Int64 absolute B;
begin
  result := A64-B64;
end;

Esto evitará cualquier error de redondeo durante la comparación (trabajando con valores enteros * 10000), y será más rápido que la implementación predeterminada basada en FPU (especialmente bajo el compilador XE2 de 64 bits).

Consulte este artículo para obtener información adicional .

Si su situación es como la mía, este enfoque podría resultarle útil. Trabajo principalmente en nómina. Si una empresa tiene 3 departamentos y quiere cobrar el costo de un empleado equitativamente entre esos tres departamentos, hay muchas ocasiones en que habrá problemas de redondeo.

Lo que he estado haciendo es recorrer los departamentos cobrando a cada uno un tercio del costo total y agregando el costo cargado a una variable de subtotal (moneda). Pero cuando la variable de bucle es igual al límite, en lugar de multiplicar por la fracción, resto la variable subtotal del costo total y la coloco en el último departamento. Dado que las entradas de diario que resultan de este proceso siempre tienen que equilibrarse, creo que siempre ha funcionado.

Ver hilo:

D7 / DUnit: todas las pruebas CheckEquals (moneda, moneda) fallan repentinamente ...

https://forums.codegear.com/thread.jspa?threadID=16288

Parece que un cambio en nuestras estaciones de trabajo de desarrollo provocó el fracaso de la comparación de divisas. No hemos encontrado la causa raíz, pero en dos computadoras que ejecutan Windows 2000 SP4, e independientemente de la versión de gds32.dll (InterBase 7.5.1 o 2007) y Delphi (7 y 2009), esta línea

TIBDataBase.Create(nil);

cambia el valor de a 8087 palabra de control de $ 1372 a $ 1272 ahora.

Y todas las comparaciones de divisas en las pruebas unitarias fallarán con mensajes divertidos como

Expected: <12.34> - Found: <12.34>

El gds32.dll no se ha modificado, por lo que supongo que hay una dependencia en esta biblioteca a un dll de terceros que modifica la palabra de control.

Para evitar posibles problemas con el redondeo de moneda en Delphi, use 4 lugares decimales.

Esto asegurará que nunca tenga problemas de redondeo cuando realice cálculos con cantidades muy pequeñas.

" He estado allí. Hecho eso. Escrito las pruebas unitarias. & Quot;

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top