2014-11-02 16 views
5

W moim przypadku użycia zapisuję liczby w dokumencie JSON, w którym minimalizacja rozmiaru jest ważniejsza niż precyzja bardzo małych/dużych liczb. Liczby zwykle reprezentują typowe jednostki, takie jak milisekundy lub metry, które zwykle mieszczą się w zakresie [0.001,1000].Maksymalna liczbowa gęstość informacji z printf

Zasadniczo chciałbym ustawić maksymalną długość znaku. Na przykład, jeśli limit zawiera pięć znaków, wówczas:

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 

Ten przypadek testowy wydaje się zawierać najwięcej informacji w ramach ograniczenia długości.

Nie udaje się z liczbami podniesionymi do uprawnień spoza zakresu [-99,999], a zakres ten będzie różny w zależności od nałożonego ograniczenia. Być może przypadek awarii tutaj jest po prostu napisać dłuższy ciąg w tych rzadkich przypadkach.

Jest to ideał, chociaż mógłbym żyć bez jego implementacji, jeśli inne rozwiązanie jest względnie bliskie, być może obcięcie zamiast zaokrąglania, a nie wykorzystanie zapisu naukowego/potęgowanego.

EDIT oto co printf z %.3f, %.3g, %.4g produktów przez porównanie (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

Z pewnością pierwszy przykład jest lepiej wyrażony jako "123e4"? – NPE

+0

@NPE masz rację - poprawiono i dodano kilka informacji o potencjalnych przypadkach niepowodzenia na bardzo małych/dużych numerach. –

+0

Czy 'printf' z' "% .4g" 'wystarczająco blisko? (W niektórych przypadkach jest zbyt długi.) – mafso

Odpowiedz

1

dla pakowania numerów w pewnym zakresie do najmniejszej unsigned integer:

1) odlicza najmniejsza możliwa wartość. Na przykład, jeśli liczba może wynosić od 0,001 do 100 000, a konkretna liczba to 123.456, odjąć 0,001, aby uzyskać 123.455

2) Podziel według precyzji, na której Ci zależy. Na przykład, jeśli zależy Ci na tysięcznych częściach, podziel je przez 0,001. W tym przypadku liczba 123.455 staje się 123455

Po wykonaniu tej czynności i uzyskaniu najmniejszej szerokości całkowitej bez znaku skonwertuj ją na cyfry szesnastkowe (lub "32 znaki"). Dla powyższego przykładu 0,001 stanie się 0x00000000, 123.456 stanie się 0x0001E23F, a 100000 stanie się 0x05F5E0FF.

Jeśli chcesz "zmienną precyzję", możesz dodać trzeci krok, który dzieli niepodpisaną liczbę całkowitą na formularz "wartość i liczba zmian". Na przykład:

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

Następnie można łączyć z czymś value = (value << 4) | shift_count.

W ten sposób można skompresować liczby do 4 cyfr szesnastkowych. Dla powyższych przykładów 0,001 stanie się wartością 0x0000 (dokładnie reprezentującą 0,001), 123,456 stanie się wartością 0xF115 (faktycznie reprezentującą 123.425), a 100000 stanie się wartością 0xBEBF (faktycznie reprezentującą 99975.169).

+0

Dzięki. To wygląda jak [arytmetyka z ustalonym punktem] (https://en.wikipedia.org/wiki/Fixed-point_arithmetic). Jednak tak naprawdę chcę mieć ciąg znaków, który podałem w pytaniu. Klient jest przeglądarką, która odbiera JSON za pośrednictwem WebSockets. Nie mogę wymagać od klienta rozpakowania numerów, zwłaszcza, że ​​nie jest jasne, które z pól danych niestrukturalnych są liczbami i powinny zostać rozpakowane. Gdybym szedł drogą inteligentnego kodowania, użyłbym czegoś takiego jak msgpack lub protobuf lub podobnego. Na razie chcę tylko wiedzieć, czy istnieje sensowny interfejs API dla tego, co opisuję (lub w pobliżu) za pomocą standardowego C/C++. –

1

Wygląda na to, że trzeba napisać własną procedurę konwersji. Pomocna może być funkcja biblioteczna ecvt.

Ale po prostu używałbym formatu %.3g lub %.4g, usuwam zbędny znak plus i początkowe zera przed wykładnikiem i nazywam to dniem. To głównie pozostawia pewne punkty dziesiętne, które można zoptymalizować. Ponieważ jesteś tak zaniepokojony rozmiarem twojej odpowiedzi JSON, prawdopodobnie i tak będziesz używał kompresji HTTP, więc wątpię, by to spowodowało wiele narzutów.