Ir al contenido


Foto

Campo "tipo Bits" en Firebird


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

#1 santiago14

santiago14

    Advanced Member

  • Miembros
  • PipPipPip
  • 334 mensajes
  • LocationCerrillos - Salta - Argentina

Escrito 05 mayo 2017 - 02:41

Buenas, me explico.
Necesito poner en un campo de una tabla los números que me salen de un sorteo. Es decir, siempre salen 10 números en cada sorteo (sobre un universo de 90 números). La idea era poner un 0 o un 1 según el valor que haya salido en la posición correcta. 
De ahí, si salieron los números: 10, 12, 14, 16; tendría algo así. (El universo lo hago de 20 para achicar la cosa)
 
00000000010101010000 <-- Hay 20 posiciones y los "1" marcan los lugares que salieron sorteados.
 
Ahora bien, este tipo de notación ¿cómo la pongo en Firebird 2.5? ¿Solamente tengo la opción de un varchar(20)? Teniendo en cuenta que después tengo que hacer algunas cosas con eso, como por ejemplo: "decir cuáles son los números que salieron en pantalla".
Una de las cosas que también debo hacer con esto en un "AND". Esto sería, cierta progresión de números (de 20 lugares, 0 o 1) contra otras que están almacenadas (de 20 lugares, 0 o 1)
 
Si tengo otra progresión, digamos: 00100100010010000000; tengo que hacer (00100100010010000000 AND 00000000010101010000) y ver el que salió.
 
¿Cómo es conveniente guardar estos datos para luego poder manipularlos de la mejor manera?
Bueno, espero haber sido claro.
 
Gracias.

  • 0

#2 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 05 mayo 2017 - 05:11

 

Buenas, me explico.
Necesito poner en un campo de una tabla los números que me salen de un sorteo. Es decir, siempre salen 10 números en cada sorteo (sobre un universo de 90 números). La idea era poner un 0 o un 1 según el valor que haya salido en la posición correcta. 
De ahí, si salieron los números: 10, 12, 14, 16; tendría algo así. (El universo lo hago de 20 para achicar la cosa)
 
00000000010101010000 <-- Hay 20 posiciones y los "1" marcan los lugares que salieron sorteados.
 
Ahora bien, este tipo de notación ¿cómo la pongo en Firebird 2.5? ¿Solamente tengo la opción de un varchar(20)? Teniendo en cuenta que después tengo que hacer algunas cosas con eso, como por ejemplo: "decir cuáles son los números que salieron en pantalla".
Una de las cosas que también debo hacer con esto en un "AND". Esto sería, cierta progresión de números (de 20 lugares, 0 o 1) contra otras que están almacenadas (de 20 lugares, 0 o 1)
 
Si tengo otra progresión, digamos: 00100100010010000000; tengo que hacer (00100100010010000000 AND 00000000010101010000) y ver el que salió.
 
¿Cómo es conveniente guardar estos datos para luego poder manipularlos de la mejor manera?
Bueno, espero haber sido claro.
 
Gracias.

 

 

Puedes almacenarlo como número. 20 bits son 2,5 bytes. El tipo INTEGER es de 4 bytes asi que tienes de sobra.

El truco está en lugar de almacenar en formato binario, tenerlo en formato DECIMAL, y lo bueno es que la magia de la operación AND funciona igual con los decimales que con los enteros, lo mismo para OR y NOT.

 

¿Cómo es esto de tenerlos en formato DECIMAL? Simple: imaginemos a esa secuencia de ceros y unos como un gran y enorme número. Si la posición i-ésima contando de derecha a izquierda es un 1, simplemente sumamos 2^i. Para todo i = 0..19 en tu caso. Por ejemplo, si tuvieramos la secuencia 10110010 (hagamos de cuenta que son 8, para hacer los cálculos más llevaderos) de derecha a izquierda, vemos los uno y sumamos las potencias: numero = 2^1 + 2^4 + 2^5 + 2^7 = 2 + 16 + 32 + 128 = 178.

 

Lo único que hice es simplemente pasarlo a decimal. Para obtener la secuencia original, si te hace falta es hacer el "DegToBinary". Y como dije, es válido hacer un AND sobre el decimal que con los binarios:

 

1011010 (178) AND 10111101 (189) = 10110000 (176)

 

En ocasiones nos nublamos demasiado, cuando la solución es bastante simple. Hay que repasar las bases que vimos en las primeras clases de la facultad ;)

 

EDITO:

Tengo que admitir que no recuerdo si Delphi cuenta con funciones BinToInt, pero el IntToBin() seguro que está en la StrUtils, como así también lo es en Lazarus. La otra posibilidad es hacer cierta indirección y trabajar con hexadecimal. O de última implementarnos nosotros las propias.

 

Saludos,


  • 1

#3 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 06 mayo 2017 - 08:40

¡Dejen de teclear! ¡Paren esos dedos!

 

Ahora que vuelvo a leer el hilo, me doy cuenta que en tu implementación real necesitas un tipo numérico que sea capaz de almacenar un binario de 90 bits. Para almacenar 90bits se requieren Ceil(90/8) = 12 bytes. Y no hay tipo numérico de este tamaño. Vas a tener que almacenarlo particionado en dos UInt64: la parte alta del número en uno, y la parte baja en otro. Ahora te van a sobrar el 75% del UInt64 de la parte alta, ya que en total vas a tener 128 bits.

 

La otra es que dispongas de un UInt64 para almacenar la parte baja, y un FixedUInt (tipo numérico de 32bits sin signo independiente del bitness del equipo) para la alta y de esa forma tienes 64 + 32 = 96bits y no "desperdicias" tanto. Obviamente los 6 bits mal altos de la parte alta van a valer 0 siempre. Aunque por las dudas yo que vos implemento un código que evalúe que el número decimal pasado a la parte alta no permita uno mayor a 2^0 + ... + 2^25 = 67108863.

No recomiendo usar LongInt ya que es dependiente del bitness de la plataforma. Véase la documentación de los tipos.

 

¿Porqué menciono tipos numéricos sin signo? Por 3 motivos:

1) Necesitas que todos sean positivos,

2) Si por ejemplo en la parte baja emplearas Int64 estarías perdiendo la mitad del máximo número posible de almacenar en 64bits ya que un bit se destina al signo.

3) Las operaciones AND se ven afectadas por el signo. No es lo mismo hacer 145 AND 257 que -145 AND 257

 

Ahora bien esto cambia un poco las formas en que vas a necesitar hacer las operaciones binarias AND. Necesitarás hacerlo también de forma particionada:

 

ParteAltaResultado = ParteAlta1 AND ParteAlta2

ParteBajaResultado = ParteBaja1 AND ParteBaja2

 

Espero que se entienda.

 

Saludos,


  • 0

#4 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 06 mayo 2017 - 06:44

La idea de manejar binarios que representan otro tipo de dato, como pudieran ser series de booleanos o la pregunta en cuestión, puede simplificar muchas cosas y dar rapidez al código. Lo he usado para esos fines y simplifica mucho el tema. La forma de almacenar el binario es lo que puede resultar más tedioso. Para eso están las rotaciones de bits. Si quiero almacenar un uno en la posición 7, haré lo siguiente:
 


delphi
  1. Q:= 1 shl 7;

Si quiero saber si en la posición 7 tengo un uno:


delphi
  1. n:= (Q shr 7) and 1;

Si disponemos de Campos de Bits, podemos simplificar el código usándolos. C dispone de forma nativa de estos campos.

 

A partir de los números obtenidos, las operaciones lógicas binarias son tan directas y rápidas como lo permite la CPU a nivel asm.

 

El problema es cuando el número de bits necesarios sobrepasa el tipo más largo, en ese caso y como apunta Delphius, hay que trabajar por partes y sopesar si el esfuerzo sigue mereciendo la pena.

 

 

Saludos.


  • 0

#5 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 22 mayo 2017 - 12:55

Amigo Escafandra, se que ha pasado un par de días desde que hemos intervenido en el hilo, pero me ha quedado pensando en esto último que dijiste:


hay que trabajar por partes y sopesar si el esfuerzo sigue mereciendo la pena.

 

Saludos.

 

¿Tanto pecado puede ser tener que aplicar operaciones particionadas como estas?

¿Cuándo no sería viable, o cuyo "costo" sea prohibitivo?

 

Me ha quedado un poco de curiosidad esto porque justamente para un proyecto que estoy en análisis, estoy por considerar operaciones particionadas de este estilo.

 

Saludos,


  • 0

#6 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 22 mayo 2017 - 02:54

Amigo Escafandra, se que ha pasado un par de días desde que hemos intervenido en el hilo, pero me ha quedado pensando en esto último que dijiste:

 

¿Tanto pecado puede ser tener que aplicar operaciones particionadas como estas?

¿Cuándo no sería viable, o cuyo "costo" sea prohibitivo?

 

Me ha quedado un poco de curiosidad esto porque justamente para un proyecto que estoy en análisis, estoy por considerar operaciones particionadas de este estilo.

 

Saludos,

 

 

Las operaciones de particionar los datos e introducirlos en variables DWORD o QWORD posicionando los bits uno a uno pueden consumir recursos y ofuscación del código que hay que valorar despacio. No todos los compiladores se comportan de la misma forma a la hora de optimizar el código y habrá que cronometrar para salir de dudas. Si las operaciones binarias a llevar a cabo, una vez "rellenados los bits", no contemplan acarreo (como son las operaciones lógicas binarias and, or, not y xor) no tendremos grandes problemas al realizaras, pero si sumamos, restamos, rotamos o desplazamos bits más allá del límite de la variable que contiene nuestros bits, tendremos que pensar en un acarreo e implementarlo.

 

Dado que todo dependerá del código concreto que necesitemos, de los tamaños y del comportamiento del compilador, tendremos que analizar hasta que punto estas técnicas ayudan, o no, para el caso concreto que estemos tratando.

 

No recuerdo haber usado estos trucos en Delphi (si con lógica binaria en variables estándar), aunque si lo he hecho en C, con lo que no puedo establecer comparaciones entre los lenguajes y compiladores.

 

 

Saludos.


  • 2

#7 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 22 mayo 2017 - 04:53

Las operaciones de particionar los datos e introducirlos en variables DWORD o QWORD posicionando los bits uno a uno pueden consumir recursos y ofuscación del código que hay que valorar despacio. No todos los compiladores se comportan de la misma forma a la hora de optimizar el código y habrá que cronometrar para salir de dudas. Si las operaciones binarias a llevar a cabo, una vez "rellenados los bits", no contemplan acarreo (como son las operaciones lógicas binarias and, or, not y xor) no tendremos grandes problemas al realizaras, pero si sumamos, restamos, rotamos o desplazamos bits más allá del límite de la variable que contiene nuestros bits, tendremos que pensar en un acarreo e implementarlo.

 

Dado que todo dependerá del código concreto que necesitemos, de los tamaños y del comportamiento del compilador, tendremos que analizar hasta que punto estas técnicas ayudan, o no, para el caso concreto que estemos tratando.

 

No recuerdo haber usado estos trucos en Delphi (si con lógica binaria en variables estándar), aunque si lo he hecho en C, con lo que no puedo establecer comparaciones entre los lenguajes y compiladores.

 

 

Saludos.

 

Ummm. Vaya ahora que mencionaste acarreo me has dejado pensando. No he considerado ese temita.

Tendré que revisar bien que operaciones se van a efectuar.

Hasta ahora lo que estuve analizando es justamente aprovechar el "truco" que comenté en este hilo de almacenar el número como decimal. De hecho si no fuera porque es lo que ya venía haciendo no hubiera sido posible responder a este hilo.

Mi caso es algo diferente, mi gran número decimal se descompone en varios menores. Para ciertos casos serán 3, 4, 5 ... N trozos. Lo que tienen en común todos los casos es que la suma de bytes de sus trozos serán iguales. Entonces me imaginé en la posibilidad de almacenar un único gran número y "particionar" según la cantidad y tamaños necesarios.

Por ejemplo: digamos que el total de bits es de 64.

Caso #1: 4 trozos de 16 bits c/u

Caso #2: 3 trozos: trozo #2.1 de 32 bits, trozo #2.2 de 28 bits, y trozo #2.3 de 4 bits. 32 + 28 + 4 = 64 bits.

 

Es como una forma de codificación/compresión de la info. Si tuviera que almacenar (en forma persistente) la info separada en trozos estaría, en ciertas ocasiones, desperdiciando bits (sino es bytes). Si es posible codificarla en una sola gran tira y "concatenar" la data es posible ahorrar esos bits. Uno puede pensar que son algunos bits... multipliquen por unos cientos de items que van a ser almacenados y vean.  ;)  Por ejemplo para el caso #2, requerimos físicamente (memoria ram) variables de por lo menos los siguientes tamaños en bytes: 4, 4, y 1. respectivamente. Un total de 9 bytes, o 72 bits.

De los 72 en realidad necesitamos el 88,88% y en forma concreta estamos desperdiciamos unos 8 bits, o 1 byte.

Al recuperar (leer) el dato de 64 bits, los pasamos a las variables reales necesarias. La compresión la obtengo en el caso de almacenamiento persistente.

 

Tendré que ver como me puede afectar las operaciones que intervengan en los trozos. O bien asegurarme de que el tamaño para cada trozo contemple acarreos, signos, etc. Hasta cierto punto creo que puedo controlar esto ya que algunos de los "tipos ficticios" tienen un rango delimitado de posibles valores.

 

Saludos,


  • 0




IP.Board spam blocked by CleanTalk.