compression - punto - Comprimir datos de coma flotante
punto flotante plc (7)
Aquí hay algunas ideas si desea crear su propio algoritmo simple:
- Use xor del valor actual con el valor anterior para obtener un conjunto de bits que describa la diferencia.
- Divida esta diferencia en dos partes: una parte son los "bits de mantisa" y una parte son los "bits de exponente".
- Utilice la codificación de longitud variable (diferente número de bits / bytes por valor) o cualquier método de compresión que elija para guardar estas diferencias. Puede usar flujos separados para mantisas y exponentes, ya que las mantisas tienen más bits para comprimir.
- Es posible que esto no funcione bien si alterna entre dos fuentes de flujos de tiempo-valor diferentes. Por lo tanto, puede que tenga que comprimir cada fuente en una secuencia o bloque por separado.
- Para perder precisión, puede soltar los bits o bytes menos significativos de la mantisa, dejando el exponente intacto.
¿Hay algún método de compresión sin pérdida que se pueda aplicar a datos de coma flotante de series de tiempo, y superará significativamente, digamos, escribir los datos como binarios en un archivo y ejecutarlos a través de gzip?
La reducción de la precisión puede ser aceptable, pero debe suceder de forma controlada (es decir, debo ser capaz de establecer un límite de cuántos dígitos se deben guardar)
Estoy trabajando con algunos archivos de datos grandes que son series de double
correlacionados, que describen una función del tiempo (es decir, los valores están correlacionados). Generalmente no necesito la double
precisión completa, pero podría necesitar más que float
.
Dado que existen métodos especializados sin pérdidas para imágenes / audio, me preguntaba si existe algo especializado para esta situación.
Aclaración: estoy buscando herramientas prácticas existentes en lugar de un documento que describa cómo implementar algo como esto. Algo similar a gzip en velocidad sería excelente.
Dado que usted declara que necesita una precisión en algún lugar entre ''flotante'' y ''doble'': puede poner a cero cualquier cantidad de bits menos significativos en flotantes de precisión simple y doble. Los numers de coma flotante IEEE-754 se representan binaria aproximadamente como seeefffffffff
, que representa el valor
signo * 1.fffffff * 2 ^ (eee).
Puede poner a cero la fracción menos significativa (f) bits. Para los flotadores de precisión simple (32 bits), hay 23 fracciones de los cuales puede poner a cero hasta 22. Para la precisión doble (64 bits), es 52 y hasta 51. (Si pone a cero todos los bits , entonces los valores especiales NaN y +/- inf se perderán).
Especialmente si los datos representan valores decimales como 1.2345, esto ayudará a la compresión de datos. Esto se debe a que 1.2345 no se puede representar exactamente como un valor de punto flotante binario, sino más bien como 0x3ff3c083126e978d
, que no es 0x3ff3c083126e978d
con la compresión de datos. Cortar los 24 bits menos significativos dará como resultado 0x3ff3c08312000000
, que sigue siendo exacto a aproximadamente 9 dígitos decimales (en este ejemplo, la diferencia es 1.6e-9).
Si hace esto en los datos brutos, y luego almacena las diferencias entre los números subsiguientes, será aún más amigable con la compresión (a través de gzip) si los datos brutos varían lentamente.
Aquí hay un ejemplo en C:
#include <inttypes.h>
double double_trunc(double x, int zerobits)
{
// mask is e.g. 0xffffffffffff0000 for zerobits==16
uint64_t mask = -(1LL << zerobits);
uint64_t floatbits = (*((uint64_t*)(&x)));
floatbits &= mask;
x = * ((double*) (&floatbits));
return x;
}
Y uno en python / numpy:
import numpy as np
def float_trunc(a, zerobits):
"""Set the least significant <zerobits> bits to zero in a numpy float32 or float64 array.
Do this in-place. Also return the updated array.
Maximum values of ''nzero'': 51 for float64; 22 for float32.
"""
at = a.dtype
assert at == np.float64 or at == np.float32 or at == np.complex128 or at == np.complex64
if at == np.float64 or at == np.complex128:
assert nzero <= 51
mask = 0xffffffffffffffff - (1 << nzero) + 1
bits = a.view(np.uint64)
bits &= mask
elif at == np.float32 or at == np.complex64:
assert nzero <= 22
mask = 0xffffffff - (1 << nzero) + 1
bits = a.view(np.uint32)
bits &= mask
return a
Es posible que desee echar un vistazo a estos recursos:
- Compresión sin pérdida de valores de punto flotante pronosticados
- Papers by Martin Burtscher : El algoritmo de compresión de coma flotante de doble precisión FPC y su implementación , compresión rápida sin pérdidas de datos científicos de coma flotante y compresión de alto rendimiento de datos de coma flotante de doble precisión
También es posible que desee probar Logluv-TIFF comprimido para esto, pensé que no los había usado yo mismo.
Posibles métodos que se pueden usar para la compresión de coma flotante:
Transposición 4xN para flotación y 8xN para doble + lz77
Implementación: compresión de punto flotante en TurboTranspose
ver también la compresión con pérdida limitada por errorPredictor (por ejemplo, método de contexto finito) + codificación (por ejemplo, "compresión de enteros").
Implementación: compresión de punto flotante en TurboPForcuando sea posible, convierta todos los números de punto flotante en enteros (por ejemplo, 1.63 -> 163),
luego usa compresión de enteros
Implementación: compresión de enteros
Puede probar todos estos métodos, con sus datos, utilizando la herramienta icapp para Linux y Windows.
Puede usar el algoritmo de suavizado exponencial de Holt (que es un algoritmo de compresión basado en la predicción). Inicialmente, asigne un poco de peso a los datos y pronostique el siguiente valor. Si ambos datos son los mismos, produce muchos ceros en el MSB al hacer la operación XOR.
Una de las técnicas que usan los HDF5 es "barajar", donde se agrupan cada byte para N valores de coma flotante. Es más probable que esto le proporcione secuencias repetitivas de bytes que se comprimirán mejor con gzip, por ejemplo .
Un segundo método que he encontrado que reduce en gran medida el tamaño de los datos comprimidos con gzip es convertir primero los datos al formato float16 (media precisión) y viceversa a float32. Esto produce muchos ceros en la secuencia de salida que puede reducir el tamaño de los archivos en un 40-60 por ciento después de la compresión. Una sutilidad es que el valor máximo de float16 es bastante bajo, por lo que es posible que desee escalar sus datos primero, por ejemplo, en python
import numpy as np
import math
input = np.array(...)
# format can only hold 65504 maximum, so we scale input data
log2max = int(math.log(np.nanmax(input), 2))
scale = 2**(log2max - 14)
scaled = input * (1./scale)
# do the conversion to float16
temp_float16 = np.array(scaled, dtype=np.float16)
# convert back again and rescale
output = np.array(temp_float16, dtype=np.float32) * scale
Algunas pruebas sugieren que la diferencia fraccional absoluta media entre la entrada y la salida para algunos datos es de alrededor de 0,00019 con un máximo de 0,00048. Esto está en línea con la precisión 2 ** 11 de la mantisa.
Ya que está pidiendo herramientas existentes, tal vez zfp haga el truco.