PDA

Ver la Versión Completa : Problema de Redondeo de Decimales con una Función


agustibaldo
07-11-2008, 21:05:04
Estimados amigos de Club Delphi, en esta oportunidad les escribo porque tengo un problema bastante raro con una función que intenta obtener solo dos decimales de un valor.

Concretamente tengo lo siguiente:

function TFormPrincipal.TruncarDecimales (parametro: Double):Double;
begin
Result := (Int(parametro * 100)) / 100;
end;
la cual llamo y grabo el resultado en una variable de la siguiente manera:

IMPORTE_TOTAL := TruncarDecimales(IMPORTE);donde IMPORTE_TOTAL e IMPORTE son de tipo Doubles.

El problema se presenta cuando graba el resultado. Supongamos que, al momento de llamar a la función, IMPORTE = 190.950; cuando graba en IMPORTE_TOTAL el resultado es 190.94.-

Realmente no se que puede estar pasando. He probado declarar tanto la function como las variables como tipo Extended pero sigue persistiendo el problema.:confused:

Desde ya muchas gracias por su tiempo.
Saludos a todos.

rgstuamigo
07-11-2008, 21:27:17
hola agustibaldo, yo lo veo raro lo que estas haciendo en tu funcion TruncateDecimales;sinceramente no hace nada esa funcion por que primero lo multiplicas * 100 y luego lo divides por el mismo 100 :confused:. no surte ningun efecto con lo que esta entrando, osea lo mismo que entra esta saliendo.......No se realmente lo que quieres hacer con esa funcion......:rolleyes:
Saludos.........

ContraVeneno
07-11-2008, 22:51:14
Es broma ¿no?

multiplicas por 100 y luego divides por 100.... http://l.yimg.com/us.yimg.com/i/mesg/emoticons7/7.gifhttp://l.yimg.com/us.yimg.com/i/mesg/emoticons7/43.gifhttp://l.yimg.com/us.yimg.com/i/mesg/emoticons7/35.gif
además, ¿que tiene de malo la función "int"?
¿para que crear otra?

Lepe
07-11-2008, 23:02:02
Que sí, que la función está bien.

multiplica por 100
coge la parte entera !!
divide entre 100

(la primera vez que ví la función, también me pasé por alto el "int" y los paréntesis ;))

El segundo paso se os coló ;), que precisamente es la que elimina los posibles decimales que tenga. Así solo se queda con 2 decimales.

Yo declaraba todas las variables como Currency y así evitaba esa función y el fallo inherente a los Floats/Extended que nunca guardarán el valor que representa, siempre guardará una aproximación.

Si usas Bases de datos, cambia el tipo también a Currency o NUMERIC(10,2) (en interbase y firebird con dialecto 3).

Si eso, pregunta ;).

Saludos

ContraVeneno
07-11-2008, 23:55:43
cierto, cierto, mea culpa

En SQL Server, el tipo de dato para el campo es "money" o "decimal(10,2)"

¿"FormatFloat" hubiera servido?

coso
08-11-2008, 00:05:54
Hola, si usas Round


function TForm1.TruncarDecimales (parametro: Double): Double;
begin
Result := (Round(parametro * 100)) / 100;
end;


solucionado ;)

ContraVeneno
08-11-2008, 00:25:10
¿ la función "RoundTo" serviría para eso?

coso
08-11-2008, 00:26:55
Si. En mi delphi no esta (v.5) pero si, parece que hace exactamente eso.

ContraVeneno
08-11-2008, 00:28:54
supongo que "SimpleRoundTo" también serviría. Dependiendo del resultado final que se quiera.

Y no creo que sea dificil de implementar:

function SimpleRoundTo(const AValue: Double; const ADigit: TRoundToRange = -2): Double;
var
LFactor: Double;
begin
LFactor := IntPower(10, ADigit);
if AValue < 0 then
Result := Trunc((AValue / LFactor) - 0.5) * LFactor
else
Result := Trunc((AValue / LFactor) + 0.5) * LFactor;
end;

agustibaldo
10-11-2008, 12:08:07
Ante todo, muchas gracias por sus respuestas.

Voy a probar usar la función Round para ver si ésta resuelve mi problema. En un primer momento no se me ocurrió porque, según tengo entendido, esta función redondea decimales, y lo que yo busco es mostrar solo 2 decimales, no redondear. De todas formas, intentaré esta forma para ver que resulta. Luego les comento.

Estoy trabajando con SQL Server 2005 y el tipo del campo IMPORTE es decimal(22,7)

Un abrazo a todos...

coso
10-11-2008, 12:11:21
Si, round redondea decimales, por eso mismo transforma 1.9465*100 = 194.65 en 195, mientras que int a secas tansolo te cogeria el valor entero (194). De todas maneras, la funcion RoundTo que te comenta contraveneno hace exactamente lo que querias implementar. saludos.

agustibaldo
10-11-2008, 12:37:05
Exacto, por eso me quedaba la duda. Al utilizar la función ROUND redondeo los decimales, y para el caso en que el valor sea pej. 190.857, el resultado de esta función será 190.86, lo cual no es lo que deseo.

Voy a probar usar ROUNDTO para ver que resulta. Tenes idea cual es la diferencia entre una y otra?

Muchas gracias por sus respuestas

coso
10-11-2008, 12:39:52
si, pero si multiplicas por 100zzzZZZzzz lo que te puse funciona a la perfeccion. saludos.

Lepe
10-11-2008, 12:42:34
Pues ahora no me cuadran las cosas.

Tienes definido en la base de datos que almacene con una precisión de 7 decimales, lo cual me parece correctísimo para no perder decimales en operaciones de tanto por ciento, divisiones, etc y, ¿ahora no quieres redondear?, ¿quieres despreciar toda esa exactitud que te está dando los 7 decimales? no lo entiendo ;).

Yo lo que haría es especificar en delphi, (en los campos persistentes) la propiedad del TField Currency a true. Esto hace que se redondee a 2 decimales cuando se va a mostrar el dato, pero realmente guardará toda la precisión que quieres.

Por otra parte y al menos en españa, se permite legalmente redondear el monto total de una factura, pero no así las subpartes (iva, base imponible, etc). Tenlo en cuenta.

Otro detalle más (por si fueran pocos ;)) Mira con detenimiento las funciones RoundTo, simpleRoundTo, etc, ya que alguna de ellas depende de un parámetro de configuración del sistema, y determina si usará el tipo de redondeo (hacia el positivo más alto, hacia el positivo más bajo, etc).

Saludos

coso
10-11-2008, 12:43:36
para el caso en que el valor sea pej. 190.857, el resultado de esta función será 190.86, lo cual no es lo que deseo.

no cuadra : antes buscabas que se redondeara ¿ahora ya no? de todas maneras, tanto la funcion escrita como el RoundTo o la implementacion de contraveneno te funcionaran. saludos.

coso
10-11-2008, 12:51:19
mmmm vale, ahora te entiendo. RoundTo. Aunque correctamente para cogerte 2 decimales te tendria que redondear el ultimo. saludos.

coso
10-11-2008, 12:57:17
:D:D ... o bien esto


function TForm1.TruncarDecimales (parametro: Double): Double;
begin
Result := Floor(parametro * 100)/100;
end;


esto si que creo que es lo que querias. saludos.

agustibaldo
10-11-2008, 13:04:19
Estimados amigos, es probable que no me haya explicado bien. Por eso trataré de ser lo más claro posible.

Con respecto al cuestionamiento de [lepe] te comento que estoy trabajando con la BD de un ERP Nacional (de Argentina) que no construimos nosotros. Solo hacemos aplicaciones que manipulan datos del antes mencionado. Si bien este campo que necesito grabar tiene una precisión de 7 decimales, a los fines prácticos de la aplicación que estamos desarrollando solo se necesitan 2 decimales. Por esta razón, es que necesito obtener solo dos decimales.

Y en cuanto al planteo que me hace [coso], es probable que la palabra "redondear" no sea la mas adecuada para este caso y se pueda prestar a confusiones. Concretamente necesito "truncar" la cantidad de decimales que tiene un valor a 2. Algunos ejemplos:

Si tengo 190.253, necesito que el resultado sea 190.25
Si tengo 190.250, necesito que el resultado sea 190.25
Si tengo 190.258, necesito que el resultado sea 190.25

Es decir, necesito truncar la cantidad de decimales que muestra. Y grabar este resultado.

Lamento que haya surgido esta confusión y espero, esta vez, haber sido claro. Si no entienden algo por favor escriban y lo vuelvo a explicar.

Desde ya, muchísimas gracias por su tiempo.
Un abrazo.

coso
10-11-2008, 13:08:22
No no tranquilo :) el ultimo codigo, con floor (unit math), trunca. Tambien existe la funcion trunc, pero no se porque obtenia el mismo resultado que con int. Debe ser por la imprecisión de los doubles. saludos.

agustibaldo
10-11-2008, 13:13:03
la verdad, [coso], no se como agradecerte. Genio total!!!
Probé FLOOR y funciona perfectamente.

Muchas gracias a todos los que me dieron una mano con esto.
Un abrazo.