2014-11-02 14 views
5

Il mio caso d'uso sta scrivendo numeri in un documento JSON in cui la minimizzazione delle dimensioni è più importante della precisione di numeri molto piccoli/grandi. I numeri rappresentano comunemente unità comuni come millisecondi o metri, che tendono a cadere nell'intervallo [0,001,1000].Densità di informazione numerica massima con printf

In sostanza, mi piacerebbe impostare una lunghezza massima del carattere. Ad esempio, se il limite fosse cinque caratteri, quindi:

caso
from  to 

1234567 123e4 
12345.6 12346 
1234.56 1235 
123.456 123.5 
12.3456 12.35 
1.23456 1.235 
1.23450 1.235 
1.23400 1.234 
1.23000 1.23 
1.20000 1.2 
1.00000 1 
0.11111 0.111 
0.01111 0.011 
0.00111 0.001 
0.00011 11e-4 
0.00001 1e-5 
0.11111 0.111 
0.01111 0.011 
0.00111 0.001 
0.00011 11e-4 
0.00001 1e-5 

Questo test sembra trasmettere la maggior parte delle informazioni all'interno di un vincolo di lunghezza.

Fallisce con i numeri elevati a potenze esterne all'intervallo [-99.999] e tale intervallo varia in base alla restrizione imposta. Forse il caso di fallimento qui è solo scrivere una stringa più lunga in questi rari casi.

Questo è l'ideale, anche se potrei vivere senza implementarlo io stesso se un'altra soluzione è relativamente vicina, forse troncando invece di arrotondare e non sfruttando la notazione scientifica/esponenziata.

EDIT ecco cosa printf con %.3f, %.3g, %.4g prodotti per confronto (code here):

printf("%.3f"); 

match 0 - 1.23457e+06 -> 1234567.000 expected 12e5 
match 0 - 12345.6  -> 12345.600 expected 12346 
match 0 - 1234.56  -> 1234.560 expected 1235 
match 0 - 123.456  -> 123.456  expected 123.5 
match 0 - 12.3456  -> 12.346  expected 12.35 
match 1 - 1.23456  -> 1.235 
match 0 - 1.2345  -> 1.234  expected 1.235 
match 1 - 1.234  -> 1.234 
match 0 - 1.23  -> 1.230  expected 1.23 
match 0 - 1.2   -> 1.200  expected 1.2 
match 0 - 1   -> 1.000  expected 1 
match 1 - 0.11111  -> 0.111 
match 1 - 0.01111  -> 0.011 
match 1 - 0.00111  -> 0.001 
match 0 - 0.00011  -> 0.000  expected 11e-4 
match 0 - 1e-05  -> 0.000  expected 1e-5 
match 1 - 0.11111  -> 0.111 
match 1 - 0.01111  -> 0.011 
match 1 - 0.00111  -> 0.001 
match 0 - 0.00011  -> 0.000  expected 11e-4 
match 0 - 1e-05  -> 0.000  expected 1e-5 

printf("%.3g"); 

match 0 - 1.23457e+06 -> 1.23e+06 expected 12e5 
match 0 - 12345.6  -> 1.23e+04 expected 12346 
match 0 - 1234.56  -> 1.23e+03 expected 1235 
match 0 - 123.456  -> 123  expected 123.5 
match 0 - 12.3456  -> 12.3  expected 12.35 
match 0 - 1.23456  -> 1.23  expected 1.235 
match 0 - 1.2345  -> 1.23  expected 1.235 
match 0 - 1.234  -> 1.23  expected 1.234 
match 1 - 1.23  -> 1.23 
match 1 - 1.2   -> 1.2 
match 1 - 1   -> 1 
match 1 - 0.11111  -> 0.111 
match 0 - 0.01111  -> 0.0111 expected 0.011 
match 0 - 0.00111  -> 0.00111 expected 0.001 
match 0 - 0.00011  -> 0.00011 expected 11e-4 
match 0 - 1e-05  -> 1e-05  expected 1e-5 
match 1 - 0.11111  -> 0.111 
match 0 - 0.01111  -> 0.0111 expected 0.011 
match 0 - 0.00111  -> 0.00111 expected 0.001 
match 0 - 0.00011  -> 0.00011 expected 11e-4 
match 0 - 1e-05  -> 1e-05  expected 1e-5 

printf("%.4g"); 

match 0 -> 1.23457e+06 -> 1.235e+06 expected 12e5 
match 0 -> 12345.6  -> 1.235e+04 expected 12346 
match 1 -> 1234.56  -> 1235 
match 1 -> 123.456  -> 123.5 
match 1 -> 12.3456  -> 12.35 
match 1 -> 1.23456  -> 1.235 
match 0 -> 1.2345  -> 1.234  expected 1.235 
match 1 -> 1.234  -> 1.234 
match 1 -> 1.23  -> 1.23 
match 1 -> 1.2   -> 1.2 
match 1 -> 1   -> 1 
match 0 -> 0.11111  -> 0.1111 expected 0.111 
match 0 -> 0.01111  -> 0.01111 expected 0.011 
match 0 -> 0.00111  -> 0.00111 expected 0.001 
match 0 -> 0.00011  -> 0.00011 expected 11e-4 
match 0 -> 1e-05  -> 1e-05  expected 1e-5 
match 0 -> 0.11111  -> 0.1111 expected 0.111 
match 0 -> 0.01111  -> 0.01111 expected 0.011 
match 0 -> 0.00111  -> 0.00111 expected 0.001 
match 0 -> 0.00011  -> 0.00011 expected 11e-4 
match 0 -> 1e-05  -> 1e-05  expected 1e-5 
+0

Sicuramente il primo esempio è meglio espresso come '123e4'? – NPE

+0

@NPE hai ragione - ha emendato e incluso alcune informazioni su potenziali casi di errore su numeri molto piccoli/grandi. –

+0

'printf' con' "% .4g" 'abbastanza vicino? (In alcuni casi è troppo lungo.) – mafso

risposta

1

per il confezionamento numeri entro un certo intervallo nel più piccolo intero senza segno:

1) Sottrarre il più piccolo valore possibile. Ad esempio, se i tuoi numeri possono variare da 0,001 a 100000 e un numero specifico è 123,456, sottrarre 0,001 per ottenere 123,455

2) Dividi per la precisione che ti interessa. Ad esempio, se ti importa di millesimi, dividi per 0,001. In questo caso il numero 123.455 diventa 123455

Una volta fatto questo e avere il numero intero senza segno di larghezza minore, convertirlo in cifre esadecimali (o forse "cifre base 32"). Per l'esempio sopra, 0.001 diventerebbe 0x00000000, 123.456 diventerebbe 0x0001E23F e 100000 diventerebbe 0x05F5E0FF.

Se si desidera "precisione variabile", è possibile aggiungere una terza fase che divide il valore intero senza segno in forma "valore e numero di turni". Per esempio:

shift_count = 0; 
    while(value > 0xFFF) { 
     value = value >> 1; 
     shift_count++; 
    } 

Quindi è possibile concatenare con qualcosa di simile value = (value << 4) | shift_count.

In questo modo, è possibile comprimere i numeri fino a 4 cifre esadecimali. Per gli esempi sopra, 0.001 diventerebbe 0x0000 (che rappresenta esattamente 0.001), 123.456 diventerebbe 0xF115 (che rappresenta effettivamente 123.425) e 100000 diventerebbe 0xBEBF (che rappresenta effettivamente 99975.169).

+0

Grazie. Sembra che [aritmetica in virgola fissa] (https://en.wikipedia.org/wiki/Fixed-point_arithmetic). Tuttavia, in realtà voglio la forma della stringa che ho dato nella domanda. Il client è un browser che riceve JSON tramite WebSockets. Non posso richiedere al cliente di decomprimere i numeri, soprattutto perché non è chiaro quale dei campi dei dati non strutturati siano numeri e debba essere decompresso. Se avessi intenzione di percorrere la strada della codifica intelligente, userei qualcosa come msgpack o protobuf o simili. Per ora voglio solo sapere se c'è un'API ragionevole per quello che descrivo (o vicino ad esso) usando C/C++ standard. –

1

Sembra che tu debba scrivere la tua routine di conversione. La funzione di libreria ecvt potrebbe essere di aiuto.

Ma utilizzare semplicemente il formato %.3g o %.4g, rimuovere il segno più superfluo e gli zeri iniziali prima dell'esponente e chiamarlo un giorno. Questo lascia principalmente alcuni punti decimali che potrebbero essere ottimizzati. Dato che sei così preoccupato delle dimensioni della tua risposta JSON, probabilmente utilizzerai comunque la compressione HTTP, quindi dubito che ciò causerà un sovraccarico.