Ir al contenido


Foto

Calcular el resto de la división entera (Mod) de un número de 20 dígitos en Firebird 2.0


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

#1 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 13 abril 2016 - 06:00

Hola amigos,

 

Me gustaría calcular en PSQL el resto de la división entera de un número de 20 dígitos y me encuentro con el problema de que ese número es muy grande.

 

La función MOD que viene en ib_udf solo trabaja con integers, y aunque trabajase con bigints, esos solo permiten almacenar números de 19 dígitos.

 


php
  1. DECLARE EXTERNAL FUNCTION mod
  2.     INTEGER, INTEGER
  3.     RETURNS DOUBLE PRECISION BY VALUE
  4.     ENTRY_POINT 'IB_UDF_mod' MODULE_NAME 'ib_udf';

 

¿ Conocéis un algoritmo para calcular la función MOD para un número muy grande ?. En concreto quiero calcular el IBAN de unos números de cuenta, y para ello tengo que calcular el resto de la división entera de cada número de cuenta (són 20 dígitos) por 97.

 

Gracias.

 

 


  • 0

#2 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 13 abril 2016 - 02:16

No veo otra posibilidad que lo hagas vía sistema y no por el lado de PSQL.

Para esa cantidad de dígitos lo más adecuado es que emplees una biblioteca de precisión arbitraria. Estas bibliotecas ya cuentan con operaciones diseñadas para trabajar con números grandes, y seguramente tienen en su haber un MOD.

Luego a ese número la yo puedes meter en una variable INT64 (El resto de un número de 20 dígitos por 97 ya es cuanto mucho de 18 dígitos, si la matemática no me falla)

 

No te sabría sugerir alguna biblioteca de ese tipo en Delphi la verdad. Se que hay. Una búsqueda rápida me llevó por ejemplo a esto.

 

Desconozco si algo como PythonForDelphi (O alguna otro framework de este estilo para otro lenguaje de precisión arbitraria) tiene la capacidad de devolver un resultado numérico y pasarlo a una variable por ejemplo. O si es únicamente para ejecutar scripts.

 

Saludos,


  • 1

#3 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 14 abril 2016 - 09:15

Tienes razón, he abandonado la idea de hacerlo en el PSQL y he trasladado el cálculo al código Delphi,una vez traídos los datos.

 

Gracias.


  • 1

#4 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 14 abril 2016 - 03:34

Tienes razón, he abandonado la idea de hacerlo en el PSQL y he trasladado el cálculo al código Delphi,una vez traídos los datos.

 

Gracias.

 

¿Utilizaste alguna biblioteca de precisión arbitraria? Porque no se si con jugar con el extended te garantice precisión en los dos últimos dígitos. ¿O como lo encaraste? Me pica la curiosidad.

 

saludos,


  • 0

#5 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 15 abril 2016 - 08:17

¿Utilizaste alguna biblioteca de precisión arbitraria? Porque no se si con jugar con el extended te garantice precisión en los dos últimos dígitos. ¿O como lo encaraste? Me pica la curiosidad.
 
saludos,

 
He utilizado una función Delphi que han compartido en ClubDelphi. La verdad es que no veo bien como hace el cálculo, pero como bien dices, no hace simplemente el módulo ya que el valor no cabe en una variable Extended.

delphi
  1. function GenerarIBAN(Pais, Cuenta: string): string;
  2.  
  3.   function EsAlfanumerico(Caracter: Char): boolean;
  4.   begin
  5.     Result := (Caracter in ['A'..'Z']) or (Caracter in ['a'..'z']);
  6.   end;
  7.  
  8.   function EsNumerico(Caracter: Char): boolean;
  9.   begin
  10.     Result := (Caracter in ['0'..'9']);
  11.   end;
  12.  
  13. var
  14.   cAux,
  15.   AuxCuenta: string;
  16.   i: Integer;
  17.   auxTemp: Extended;
  18. begin
  19.   Result := '';
  20.   Cuenta := Trim(Cuenta);
  21.   if (Cuenta = '') or (Length(Cuenta) > 34) then
  22.     Exit
  23.   else
  24.   begin
  25. // Fase 1: Nos aseguramos que solo contiene Letras y Numeros
  26.     Cuenta := UpperCase(Cuenta);
  27.     cAux := '';
  28.     for i := 1 to Length(Cuenta) do
  29.     if (EsAlfanumerico(Cuenta[i]) or EsNumerico(Cuenta[i])) then
  30.       cAux := cAux + AnsiString(Cuenta[i]);
  31.     Cuenta := cAux;
  32.     AuxCuenta := Cuenta;
  33.  
  34. // Fase 2: Se comprueba si ya es un codigo IBAN, y si no lo es, se añade el PAIS
  35.     if (EsAlfanumerico(Cuenta[1]) and EsAlfanumerico(Cuenta[2])) then // Es IBAN
  36.     begin
  37.       if (EsAlfanumerico(Cuenta[3]) or EsAlfanumerico(Cuenta[4])) then
  38.         Exit;
  39.  
  40.       Pais   := Copy(Cuenta, 1, 2);//Cuenta.SubString(1, 2);
  41.       Cuenta := Copy(Cuenta, 5, Length(Cuenta));//Cuenta.SubString(5, Length(Cuenta)) + Cuenta.SubString(1, 2) + '00';
  42.       AuxCuenta := Cuenta;
  43.     end
  44.     else
  45.     begin
  46.   //y si no lo es, se añade el PAIS
  47.       if (Trim(Pais) = '') then
  48.         Pais := 'ES';
  49.     end;
  50.     Cuenta := Cuenta + Pais + '00';
  51.  
  52. // Fase 3: Se convierten las letras del pais en sus numeros equivalentes:
  53.   //A=10, B=11, C=12 ... Z=35
  54.     cAux := '';
  55.     for i := 1 to Length(Cuenta) do
  56.     begin
  57.       if (EsAlfanumerico(Cuenta[i])) then
  58.         cAux := cAux + FormatFloat('00', Ord(Cuenta[i]) - 55)//Cuenta[i - 1] - 55)
  59.       else
  60.         cAux := cAux + Copy(Cuenta, i, 1);
  61.     end;
  62.     Cuenta := cAux;
  63.  
  64. // Fase 4: Dividimos por 97
  65.     auxTemp := StrToInt(Copy(Cuenta, 1, 9)) mod 97;
  66.     cAux   := FormatFloat('0', auxTemp);//FormatFloat("0", StrToInt(Cuenta.SubString(1, 9)) % 97);
  67.     Cuenta := Copy(Cuenta, 10, Length(Cuenta));//Cuenta.SubString(10, Cuenta.Length());
  68.     while (Trim(Cuenta) <> '') do
  69.     begin
  70.       if (StrToInt(cAux) < 10) then
  71.       begin
  72.         cAux   := cAux + Copy(Cuenta, 1, 8);//Cuenta.SubString(1, 8);
  73.         Cuenta := Copy(Cuenta, 9, Length(Cuenta));//Cuenta.SubString(9, Cuenta.Length());
  74.       end
  75.       else
  76.       begin
  77.         cAux   := cAux + Copy(Cuenta, 1, 7);//Cuenta.SubString(1, 7);
  78.         Cuenta := Copy(Cuenta, 8, Length(Cuenta));//Cuenta.SubString(8, Cuenta.Length());
  79.       end;
  80.       auxTemp := StrToInt(cAux) mod 97;
  81.       cAux := FormatFloat('0', auxTemp);
  82.     end;
  83. // Fase 5: Devolvemos el IBAN completo con sus digitos de control.
  84. // Se puede cambiar para devolver solo el digito de control, o lo que se quiera.
  85.  
  86.     Result := Pais + FormatFloat('00', 98 - StrToInt(cAux)) + AuxCuenta;
  87.   end;
  88. end;

Este sería el cálculo en PSQL con campos numéricos suficientemente grandes (en este caso PostgreSQL) :

delphi
  1. CREATE OR REPLACE FUNCTION calculate_iban(account varchar)
  2. RETURNS varchar AS $
  3. DECLARE num numeric;
  4. DECLARE DC varchar(2);
  5. BEGIN
  6. /* Eliminamos Separadores */
  7.     SELECT replace(account, ' ', '') INTO account;
  8.     SELECT replace(account, '-', '') INTO account;
  9. /* Añadimos Código País España ES00 */
  10.     SELECT CAST(account || '142800' AS numeric) INTO num;
  11. /* Cálculo de los Dígitos de Control */
  12.     SELECT LPAD(CAST(98 - MOD(num, 97) AS varchar(2)), 2, '0') INTO DC;
  13. /* Devolvemos el IBAN */
  14.     RETURN 'ES' || DC || account;
  15. END;

El numero que finalmente dividimos por 97 es de 26 dígitos (20 de número de cuenta + 6 de país).
 
 
Lo bueno de tener una función en Delphi que solo utiliza variables Extended es que podría portar ese código a Firebird (aunque no acabo de entender como el cálculo es equivalente), pero habiéndolo puesto ya en Delphi, no me hace falta.
  • 1

#6 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 15 abril 2016 - 02:45

Yo tampoco me hago mucha idea de la matemática que emplea. Tendría que analizarlo detenidamente.

Si en verdad funciona esa alternativa, ¡usala!

 

Yo tengo entendido que al tipo Extended es preferible evitarlo, ya que su definición real depende de la arquitectura de la máquina. Fijate en la documentación, que puede ser de 10 bytes (Win32) u 8 (Win64).

 

Saludos,


  • 0




IP.Board spam blocked by CleanTalk.