Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Redondeo (https://www.clubdelphi.com/foros/showthread.php?t=2643)

Casimiro Notevi 21-04-2014 18:40:03

Pues usa la función que he puesto yo antes.
Y veo que ecfisa también.
Creo que con ellas no tendrás ese problema.

Al González 22-04-2014 02:07:35

En realidad, las funciones de redondeo nativas de Delphi no fueron de todo buenas en las versiones de años anteriores. En Delphi 7, por ejemplo, es evidente un bug de signo en la función SimpleRoundTo.

Delphi 7:
Código Delphi [-]
function SimpleRoundTo(const AValue: Double; const ADigit: TRoundToRange = -2): Double;
var
  LFactor: Double;
begin
  LFactor := IntPower(10, ADigit);
  Result := Trunc((AValue / LFactor) + 0.5) * LFactor;
end;
Delphi XE2:
Código Delphi [-]
function SimpleRoundTo(const AValue: Double; const ADigit: TRoundToRange = -2): Double;
var
  LFactor: Extended;
begin
  LFactor := IntPower(10.0, ADigit);
  if AValue < 0 then
    Result := Int((AValue / LFactor) - 0.5) * LFactor
  else
    Result := Int((AValue / LFactor) + 0.5) * LFactor;
end;
Viendo el código fuente de la unidad Math, noto que Embarcadero ha insertado diversos cambios relacionados con las funciones de redondeo, y las pruebas arrojan mejores resultados que en Delphi 7. Por otra parte, parece que SetRoundMode ya no tiene efecto sobre RoundTo, lo cual puede ser bueno o malo, dependiendo de si utilizábamos o no esta última para hacer operaciones de ceil y floor (como las ilustradas por Nelson en el mensaje 17).

novato_erick no deberá tener más problemas si usa una versión reciente de Delphi. Pero cabe recordarle que hay muchas funciones hechas por la Comunidad (como las que ya le sugirieron) que puede emplear de manera igualmente efectiva en versiones de Delphi como la 7.

Tal vez el siguiente código ayude a apreciar lo que comento en los párrafos anteriores. Para quien pueda hacerlo, recomiendo probarlo tanto en Delphi 7 como en XE2, pues con ello notará las diferencias. Incluyo en estas pruebas unas con ghRound (función de GHF) a fin de ofrecerle una alternativa más a nuestro compañero.
Código Delphi [-]
Uses
  Math, GHFRTL;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage (FloatToStr (RoundTo (1.235, -2)));
  ShowMessage (FloatToStr (RoundTo (-1.235, -2)));
  ShowMessage (FloatToStr (RoundTo (5.265, -2)));
  ShowMessage (FloatToStr (RoundTo (-5.265, -2)));

  ShowMessage (FloatToStr (SimpleRoundTo (1.235, -2)));
  ShowMessage (FloatToStr (SimpleRoundTo (-1.235, -2)));
  ShowMessage (FloatToStr (SimpleRoundTo (5.265, -2)));
  ShowMessage (FloatToStr (SimpleRoundTo (-5.265, -2)));

  GHMidpointRounding := ghmrInfinity;
  ShowMessage (FloatToStr (ghRound (1.235, 2)));
  ShowMessage (FloatToStr (ghRound (-1.235, 2)));
  ShowMessage (FloatToStr (ghRound (5.265, 2)));
  ShowMessage (FloatToStr (ghRound (-5.265, 2)));
end;
Finalmente, creo que vale la pena echar un vistazo a este par de antiguos hilos (tan antiguos como el proceso de pulimento de mi carácter :o):

http://www.clubdelphi.com/foros/show...573#post170573

http://www.clubdelphi.com/foros/showthread.php?t=38102

Un saludo.

Al González.

Casimiro Notevi 22-04-2014 09:27:11

Cita:

Empezado por Casimiro Notevi (Mensaje 474778)
Si quieres redondear, por ejemplo, a 2 decimales:
Código Delphi [-]
]resultado := redondeo(importe,2);

Código Delphi [-]
function Redondeo (valor: Double; decimales: integer = 0): Double;
var
  factor: Double;
begin
  factor := IntPower(10,decimales);
  //
  if valor > 0 then
    Result := (trunc((valor*factor)+0.5)) / factor
  else
    Result := (trunc((valor*factor)-0.5)) / factor;
end;

Como dos gotas de agua, "me han copiado" :cool:

novato_erick 22-04-2014 19:35:42

hola Al González cómo siempre tus aportes igual que el de tus colegas casimiro, ecfisa son un ejemplo de conocimiento y enriquecimiento en este foro los felicito y les doy las gracias..

Al González
Cita:

n realidad, las funciones de redondeo nativas de Delphi no fueron de todo buenas en las versiones de años anteriores. En Delphi 7, por ejemplo, es evidente un bug de signo en la función SimpleRoundTo.
Tengo XE disculpen por no aclarar eso desde el seguimiento con este hilo.

Algo curioso casimiro

utilicé tu función que me recomendabas y la de ecfisa también pero tengo el mismo problema no me redondean cuando el tercer valor decimal ejemplo: 5.265 u otro valores. por eso busqué la ayuda de delphi y otros hilos es extraño que no suceda muy a menudo.

ya me está volviendo loco ese redonde porque la imp fiscal si lo hace siempre para arriba y en cosasiones es 0.01 centécimos el valor que me hace falta en la factura...


Saludos chicos

pd: disculpen si no he mencionado a las otras personas que me han siempre colaborado pero son varios que olvido sus nick... pero igual manera son muy útiles sus aportes gracias chicos

Casimiro Notevi 22-04-2014 19:44:10

Pon un ejemplo concreto, con código, datos, valores, todo.
¡Y a saber cómo redondea esa impresora!

ecfisa 22-04-2014 20:20:52

Cita:

Empezado por novato_erick (Mensaje 475376)
...
en fin a la hora de utilizar la función me sale 5.26 en vez de5.27

Código Delphi [-]
 5.26 := simpleroundto(5.265, -2)

en fin necesito esto porque en mi país están utilizando las dichosas impresoras fiscales el cual ese valor de 5.265 ella misma me hace el calculo y lo guarda como 5.27 mientras que en la aplicación simpre me está mostrando el 5.26 aun utilizando la función Simpleroundto.

Hola novato_erick.

A ver... proba de este modo:
Código Delphi [-]
function Redondear(const Numero: Double; const Cifras: Integer): Extended;
var
  pd: Extended;
begin
  pd := Exp(Ln(10)*Cifras);
  if Trunc(Frac(Numero)*pd) mod 10 < 5 then
    Result:= Round(Numero*pd)/pd
  else
    Result:= Round(Numero*pd+1/pd)/pd;
end;

Ejemplo:
Código Delphi [-]
   FloatToStr(Redondear(5.265, 2)); // 5.27


Saludos :)

novato_erick 22-04-2014 21:05:58

Hola Chicos lastimosamente la impresora fiscal tiene una dll llamada BEMAFI32.dll el cual llama una funciones en el que están dentro de una unidad que la llame declaraciones y busque cómo la impresora realiza el redondeo y solamente tiene esto:

Código Delphi [-]
function Bematech_FI_ProgramaRedondeo: Integer; StdCall;
  External 'BEMAFI32.DLL';

más nada jejeje igual que otras funciones que utilizo.

en cuanto a código de ejemplo hago lo siguiente:

Código Delphi [-]
procedure TfrmFacturacionPrincipal.AgregaProductosaVenta;
Var
  codigo, Descripcion, wimpuesto, Cantidad, Precio, wdescuento: AnsiString;
  CIva: String;
  letraimpuesto: AnsiString;
  // Variable que guarda precio u x el porcentaje
  totalivapv, TotalSubVentaFac, TotalSubVentaFacU: Double; // Sin Redondeo
  totalivapvr: Double; // Con Redondeo
  DescProducto, pventapro: Double;
begin
  IdArticuloPro := 0;
  wcodigobarras := Trim(Self.eConsultaArticulo.Text);
  if wcodigobarras <> '' then
  begin
    ConsultaCantidadArt;//Consulta si hay artículos que posean cantidad
    with dmConexion.qConsultaArtInventario do
    begin
      Close;
      sql.Clear;
      sql.Add('select  a.ID_ARTICULO idAtriculo, a.COD_BARRA, a.NOMBRE, ');
      sql.Add(' a.DESCRIPCION, A.GRAVADO,a.CANTIDAD, i.PORCENTAJE, ');
      sql.Add('p.PRECIO_CIV, p.PRECIO_SIV from ARTICULOS a , ARTXIMPUES ai, ');
      sql.Add('impuesto i, PRECIO p ');
      sql.Add(' where a.ID_ARTICULO = ai.ID_ARTICULO   ');
      sql.Add('and i.ID_IMPUESTO = ai.ID_IMPUESTO ');
      sql.Add('and p.ID_ARTICULO = a.ID_ARTICULO   ');
      sql.Add('and p.ID_ARTXIMPUES = ai.ID_ARTXIMPUES');
      sql.Add('AND A.cod_barra=:pcodigo_barras');
      Params.ParamByName('pcodigo_barras').Value := wcodigobarras;
      // Asignamos una cantidad a la variable de cantidades en caso de no ponerlo con anticipacion
      try
        if wcantidad_venta = 0 then
        begin
          wcantidad_venta := 1;
        end;
        Open;
      finally
        eConsultaArticulo.Clear;
        eConsultaArticulo.SetFocus;
      end;
    end;
    IdArticuloPro := dmConexion.qConsultaArtInventarioIDATRICULO.AsInteger;
    ConsultaDescArt(IdArticuloPro);
    if DescxArt > 1 then
    begin
      precioUV := dmConexion.qConsultaArtInventarioPRECIO_SIV.AsFloat;
      precioUVSinDes := dmConexion.qConsultaArtInventarioPRECIO_SIV.AsFloat;
      DescProducto := precioUV * DescxArt / 100;
      precioUV := precioUV - DescProducto;
      precioUVSinDes := simpleroundto(precioUVSinDes, -2);
      Precio := Format('%n', [precioUVSinDes]);
    end
    else
    begin
      DescProducto := StrToFloat('0.00');
      precioUV := dmConexion.qConsultaArtInventarioPRECIO_SIV.AsFloat;
      precioUVSinDes := dmConexion.qConsultaArtInventarioPRECIO_SIV.AsFloat;
      precioUVSinDes := simpleroundto(precioUVSinDes, -2);
      Precio := Format('%n', [precioUVSinDes]);
    end;
    // Guarda a Variables la información capturada desde cantidad hasta el precio unitario
    cantv := wcantidad_venta;
    precioUV := simpleroundto(precioUV, -2);
    // llama al procedimiento para realizar calculo de cantidad por precio unitario
    CalCantxPU;
    // Insertamos en la tabla temporal cdsTempArtVenta
    if cuentaARt = cuentaARt then
    begin
      cuentaARt := cuentaARt + 1;
    end
    else
    begin
      cuentaARt := 0;
    end;
    dmlogicaventa.cdsTempArtVenta.Append;
    // se utiliza el append para ir agregando un registro despues del primer registro
    dmlogicaventa.cdsTempArtVentacdsTempArtVentaART.AsString :=
      inttostr(cuentaARt);
    dmlogicaventa.cdsTempArtVentacdsTempArtVentaIDARTICULO.AsInteger :=
      dmConexion.qConsultaArtInventarioIDATRICULO.AsInteger;
    dmlogicaventa.cdsTempArtVentacdsTempArtVentaCOD_BARRA.AsString :=
      dmConexion.qConsultaArtInventarioCOD_BARRA.AsString;
    dmlogicaventa.cdsTempArtVentacdsTempArtVentaNOMBRE.AsString :=
      dmConexion.qConsultaArtInventarioNOMBRE.AsString;
    dmlogicaventa.cdsTempArtVentacdsTempArtVentaCANTIDAD.AsString :=
      floattostr(cantv);
    dmlogicaventa.cdsTempArtVentacdsTempArtVentaPUnitario.AsFloat :=
      dmConexion.qConsultaArtInventarioPRECIO_SIV.AsFloat;
    // dataset precio sin descuento
    dmlogicaventa.cdsTempArtVentacdsTempArtVentaConDesc.AsFloat :=
      cantv * precioUVSinDes;
    // fin del dataset precio sin desc
    dmlogicaventa.cdsTempArtVentacdsTempArtVentaSUBTOTAL.AsFloat :=
      cantv * precioUV;
    dmlogicaventa.cdsTempArtVentacdsTempArtVentaDESCUENTO.AsFloat :=
      simpleroundto(DescProducto, -2);
    if dmConexion.qConsultaArtInventarioPORCENTAJE.AsString = '0' then
    begin
      exento := exento +
        dmlogicaventa.cdsTempArtVentacdsTempArtVentaSUBTOTAL.AsFloat;
      letraimpuesto := 'E';
      dmlogicaventa.cdsTempArtVentacdsTempArtVentaIMPUESTO.AsString :=
        letraimpuesto;
    end
    else
    begin
      if dmConexion.qConsultaArtInventarioPORCENTAJE.AsString = '7' then
      begin
        dmlogicaventa.cdsTempArtVentacdsTempArtVentaIMPUESTO.AsString :=
          floattostr(dmConexion.qConsultaArtInventarioPORCENTAJE.AsFloat);
        montoImpuesto := montoImpuesto +
          dmlogicaventa.cdsTempArtVentacdsTempArtVentaSUBTOTAL.AsFloat * 0.07;
      end;
      if dmConexion.qConsultaArtInventarioPORCENTAJE.AsString = '10' then
      begin
        dmlogicaventa.cdsTempArtVentacdsTempArtVentaIMPUESTO.AsString :=
          floattostr(dmConexion.qConsultaArtInventarioPORCENTAJE.AsFloat);
        montoImpuesto1 := montoImpuesto1 +
          dmlogicaventa.cdsTempArtVentacdsTempArtVentaSUBTOTAL.AsFloat * 0.10;
      end;
      if dmConexion.qConsultaArtInventarioPORCENTAJE.AsString = '15' then
      begin
        dmlogicaventa.cdsTempArtVentacdsTempArtVentaIMPUESTO.AsString :=
          floattostr(dmConexion.qConsultaArtInventarioPORCENTAJE.AsFloat);
        montoImpuesto2 := montoImpuesto2 +
          dmlogicaventa.cdsTempArtVentacdsTempArtVentaSUBTOTAL.AsFloat * 0.15;
      end;
    end;
    totalivapv := simpleroundto
      ((precioUV * dmConexion.qConsultaArtInventarioPORCENTAJE.AsFloat / 100),
      -2) * cantv; // sin redondeo
    pventapro := totalivapv + TotalCantxPU;
    dmlogicaventa.cdsTempArtVentacdsTempArtVentaPRECIOVENTA.AsFloat :=
      redondeo(pventapro, 2);
    dmlogicaventa.cdsTempArtVenta.Post;
    // Finaliza el append en el client DataSet para que el registro ingresado vaya agregandose otro
    TotalSubVentaFacU := pventapro;
    TotalSubVentaFac := TotalSubVentaFacU + pventapro;
    codigo := dmConexion.qConsultaArtInventarioCOD_BARRA.AsString;
    Descripcion := dmConexion.qConsultaArtInventarioNOMBRE.AsString;
    Impuesto := dmConexion.qConsultaArtInventarioPORCENTAJE.AsFloat;
    CIva := floattostr(Impuesto);
    ConsultaIVA := StrToInt(CIva); // variable de impuesto
    case ConsultaIVA of //La Variable wimpuesto es de tipo AnsiString por requerimiento de imp fiscal
      7:
        begin
          wimpuesto := '0700';
          eSiete.Text := FloatToStrF(montoImpuesto, ffCurrency, 12, 2);
        end;
      10:
        begin
          wimpuesto := '1000';
          eDiez.Text := FloatToStrF(montoImpuesto1, ffCurrency, 12, 2);
        end;
      15:
        begin
          wimpuesto := '1500';
          eQuince.Text := FloatToStrF(montoImpuesto2, ffCurrency, 12, 2);
        end;
      0:
        begin
          wimpuesto := 'II';
          eExentos.Text := FloatToStrF(exento, ffCurrency, 12, 2);
        end;
    end;
    total_impuestos := (montoImpuesto + montoImpuesto1 + montoImpuesto2);
    Cantidad := floattostr(cantv);
    DescProducto := simpleroundto(DescProducto, -2);
    wdescuento := floattostr(DescProducto);
    // cantidad de ventas
    wcantidad_venta := 0;
    eConsultaArticulo.SetFocus;
    // Total de venta sin descuentos
    desc := desc + DescProducto;
    eDescuentos.Text := FloatToStrF(desc, ffCurrency, 12, 2);
    subtotalventasinDesc := dmlogicaventa.cdsTempArtVentaSUBTODESVENTA.Value;
    subtotalventasinDesc := simpleroundto(subtotalventasinDesc, -2);
    eSubSinDesc.Text := FloatToStrF(subtotalventasinDesc, ffCurrency, 12, 2);
    // Total de venta pero con descuento
    subtotalventa := dmlogicaventa.cdsTempArtVentaSUBTOTALVENTA.Value;
    subtotalventa := simpleroundto(subtotalventa, -2);
    eSubTotalVenta.Text := FloatToStrF(subtotalventa, ffCurrency, 12, 2);
    // cantidad de registros en el dbgrid
    kondbgrid := dbgVentas.DataSource.DataSet.RecordCount;
    lblProductosCant.Caption := inttostr(kondbgrid);
    total_impuestos := simpleroundto(total_impuestos, -2);
    eImpuestos.Text := FloatToStrF(total_impuestos, ffCurrency, 12, 2);
    ventatotalfactura := subtotalventa + total_impuestos;
    ventatotalfactura := simpleroundto(ventatotalfactura, -2);
    eTotalVenta.Text := FloatToStrF(ventatotalfactura, ffCurrency, 12, 2);
  end;
  // Funciones de impresora fiscal bematech
  iRetorno := Bematech_FI_EspacioEntreLineas(004);
  Bematech_FI_VendeArticulo(codigo, Descripcion, wimpuesto, 'I', Cantidad, 2,
    Precio, '$', wdescuento);
  // otras impresoras de aquí en adelante
end;


Saludos

novato_erick

novato_erick 22-04-2014 21:16:25

1 Archivos Adjunto(s)
Hola una imagen para que vean pueda ser más ilustrado eso es en el caso de una variable que maneja los descuentos pero lo mismo sucede en otras variables con el redondeo.

Saludos

novato_erick

nlsgarcia 22-04-2014 21:20:18

novato_erick,

Cita:

Empezado por novato_erick
...Alguna mejor idea de funciones de redondeo que me retorne valores deseados reales...21,465 debería darme 21,47...

Revisa este código:
Código Delphi [-]
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Math;

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    ListBox2: TListBox;
    Button1: TButton;
    Label1: TLabel;
    Label2: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function RoundNumber(Number: Double; Decimal: Integer): Double;

   function DoubleDecimals(Number : Double; Decimal : Integer) : Double;
   var
      AuxNumber : String;
   begin

      AuxNumber := FloatToStr(Number);

      if Pos('.',AuxNumber) > 0 then
         Result := StrToFloat(Copy(AuxNumber,1,Pos('.',AuxNumber) + Decimal))
      else
      if Pos(',',AuxNumber) > 0 then
         Result := StrToFloat(Copy(AuxNumber,1,Pos(',',AuxNumber) + Decimal))
      else
         Result := Number;
   end;

var
   Factor: Double;

begin

   Factor := IntPower(10,Decimal);

   if Number > 0 then
      Result := DoubleDecimals((Number * Factor + 0.5) / Factor,Decimal)
   else
      Result := DoubleDecimals((Number * Factor - 0.5) / Factor,Decimal);

end;

procedure TForm1.FormCreate(Sender: TObject);
begin

   ListBox1.Items.Add('21,464');
   ListBox1.Items.Add('-21,464');
   ListBox1.Items.Add('21,465');
   ListBox1.Items.Add('-21,465');
   ListBox1.Items.Add('21,466');
   ListBox1.Items.Add('-21,466');
   ListBox1.Items.Add('3,1415927');
   ListBox1.Items.Add('-3,1415927');
   ListBox1.Items.Add('2,718281');
   ListBox1.Items.Add('-2,718281');
   ListBox1.Items.Add('5,264');
   ListBox1.Items.Add('-5,264');
   ListBox1.Items.Add('5,265');
   ListBox1.Items.Add('-5,265');
   ListBox1.Items.Add('5,267');
   ListBox1.Items.Add('-5,267');

end;

procedure TForm1.Button1Click(Sender: TObject);
var
   i : Integer;
   N1 : Double;

begin

   DecimalSeparator := ',';
   ThousandSeparator := '.';

   for i := 0 to ListBox1.Items.Count - 1 do
   begin
      N1 := StrToFloat(ListBox1.Items.Strings[i]);
      ListBox2.Items.Add(FloatToStr(RoundNumber(N1,2)));
   end;

end;

end.
El código anterior redondea un número tipo double al número de decimales especificados con redondeo al infinito, como se muestra en la siguiente imagen:



Espero sea útil :)

Nelson.

novato_erick 22-04-2014 21:37:23

hola eficsa utilice la funcion redondeo

Código Delphi [-]
    if DescxArt > 1 then
    begin
      precioUV := dmConexion.qConsultaArtInventarioPRECIO_SIV.AsFloat;
      precioUVSinDes := dmConexion.qConsultaArtInventarioPRECIO_SIV.AsFloat;
      DescProducto := precioUV * DescxArt / 100;
      precioUV := precioUV - DescProducto;
      precioUV := Redondear(precioUV, 2);//llamo la funcion de ecfisa aquí y si me redondeo  de 5.265 a 5.27
      precioUVSinDes := simpleroundto(precioUVSinDes, -2);
      Precio := Format('%n', [precioUVSinDes]);
    end
    else
    begin
      DescProducto := StrToFloat('0.00');
      precioUV := dmConexion.qConsultaArtInventarioPRECIO_SIV.AsFloat;
      precioUVSinDes := dmConexion.qConsultaArtInventarioPRECIO_SIV.AsFloat;
      precioUVSinDes := Redondear(precioUVSinDes, 2);
      Pre

implementaré la función en todos los procedimientos que necesito redondear a dos decimales despues que hago el cálculo de descuentos e impuestos creo que doy por solucionado este tema del redondeo pero que extraño que las funciones estandar que tiene delphi no realice bien esto. Pero claro todo tiene sus ventajas ya que no hay que atenerse a lo creado si no hay que crear...

Gracias Chicos en realidad muchas gracias por todo...

Saludos

novato_eric

novato_erick 22-04-2014 22:01:29

Yupiiiiiiiiiiiiiiiiiiiii Chicos listo utilicé la función en todas las variables que necesitaba y ahora si me cuadra con la impresora Fiscal y el sistema...

Chicos Agradecidos enormeeeeeeeeeeeementeeeeeeeeee por su ayuda....

Un Gran Saludos Gracias....


La franja horaria es GMT +2. Ahora son las 11:29:55.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi