Da float a stringhe binarie e viceversa con T-SQL

Potrebbe capitare l’esigenza di dover convertire dei float in stringa, ad esempio per memorizzare dei nuovi dati numerici nei campi stringa lasciati liberi in un db legacy, le cui tabelle non possono per nessun motivo essere estese o modificate.

La soluzione piu’ semplice e’ memorizzare i numeri all’interno del campo stringa nella loro rappresentazione numerica. Ad esempio, se devo memorizzare due numeri

123,4567 e 234,5678

e considerando almeno 10 caratteri per numero, la corrispondente stringa sarÃ

”  123,4567  234,5678″

Per la riconversione al valore originario, leggo la stringa a blocchi da 10 caratteri e la trasformo. Semplice, veloce e leggibile anche ad occhio nudo.

Ma questa non sempre non e’ la soluzione praticabile se lo spazio che si ha a disposizione e’ poco e se i valori dei numeri devono essere preservati nell’interezza del loro range di valori.

Prendiamo il caso  che debba memorizzare 30 quantita’ e 30 prezzi, con un dato di tipo float, per un articolo che ha una scala taglie, come una scarpa, un capo di abbigliamento o altro prodotto fashion.

Se usassi la rappresentazione numerica dei valori, e considerando almeno 16 cifre per ogni numero (virgola compresa), avrei bisogno di (30 + 30) * 16 = 960 byte. Memorizzando invece nella stringa la rappresentazione binaria di valori, necessiterei di (30 + 30) * 8 = 480 byte (il float occupa 8 byte). Certo, i dati avrebbero bisogno di una conversione apposita prima di essere trattati e sarebbero illeggibili “ad occhio nudo”, ma risparmierei la meta’ dello spazio!

Tutto dipende dai casi, ma per quei vecchi db dove ci sono magari 500 caratteri a disposizione per le ulteriori personalizzazione utente, questo giochetto ha preservato la mia sanita’ mentale. ;)

Memorizzare la rappresentazione binaria di un float in una stringa
non e’ difficile: ci pensano la funzione CAST e il tipo BINARY

CAST( CAST(@number AS BINARY(8)) AS CHAR(8))

In pratica, prima casto il numero come binario da 8 byte, e poi ricasto il risultato da binario in una stringa di 8 caratteri.

Piu’ complessa e’ invece l’operazione inversa, ovvero ritrasformare in float la stringa precedentemente ottenuta. Qui maggiori informazioni.

Alla fine, ho 2 funzioni che fanno la conversione/riconversione:

Da float a stringa compressa

CREATE FUNCTION [dbo].[fnFloatToCompressedString](@number FLOAT)
  RETURNS CHAR(8)
AS
BEGIN
  RETURN CAST( CAST(@number AS BINARY(8)) AS CHAR(8))
END

Da stringa compressa a float

CREATE FUNCTION [dbo].[fnCompressedStringToFloat](@number CHAR(8))
  RETURNS FLOAT
AS
BEGIN
  DECLARE
    @BinaryFloat BINARY(8)

  SET @BinaryFloat = CAST(@number AS BINARY(8))
  RETURN    SIGN(CAST(@BinaryFloat AS BIGINT))
    * (1.0 + (CAST(@BinaryFloat AS BIGINT) & 0x000FFFFFFFFFFFFF) * POWER(CAST(2 AS FLOAT), -52))
    * POWER(CAST(2 AS FLOAT), (CAST(@BinaryFloat AS BIGINT) & 0x7ff0000000000000) / 0x0010000000000000 - 1023)
END

E concludo con un grazie ad Alk per i suggerimenti!

Leave a Reply