[英]Maximum numerical information density with printf
我的用例是將數字寫入JSON文檔,其中大小最小化比非常小/大數字的精度更重要。 這些數字通常代表常見的單位,例如毫秒或米,它們往往落在[0.001,1000]范圍內。
基本上我想設置一個最大字符長度。 例如,如果限制為五個字符,則:
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
此測試用例似乎在長度約束內傳達了大部分信息。
它確實失敗,數字上升到超出范圍[-99,999]的范圍,並且該范圍將根據強加的限制而變化。 也許這里的失敗案例只是在這些極少數情況下寫一個更長的字符串。
這是理想的,但是如果另一個解決方案相對接近,可能會在沒有實現它的情況下生存,可能是截斷而不是舍入,而不是利用科學/指數符號。
編輯這里是什么printf
與%.3f
, %.3g
。 %.4g
, %.4g
比較產生( 代碼在這里 ):
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
用於將特定范圍內的數字打包成最小的無符號整數:
1)減去可能的最小值。 例如,如果您的數字范圍為0.001到100000且特定數字為123.456,則減去0.001以獲得123.455
2)除以你關心的精度。 例如,如果你關心千分之一,則除以0.001。 在這種情況下,數字123.455變為123455
完成此操作並使用最小寬度無符號整數后,將其轉換為十六進制數字(或者可能是“基數32位”)。 對於上面的示例,0.001將變為0x00000000,123.456將變為0x0001E23F並且100000將變為0x05F5E0FF。
如果需要“變量精度”,可以添加第三步,將無符號整數值拆分為“值和移位計數”形式。 例如:
shift_count = 0;
while(value > 0xFFF) {
value = value >> 1;
shift_count++;
}
然后你可以連接像value = (value << 4) | shift_count
value = (value << 4) | shift_count
。
這樣,您可以將數字壓縮到4個十六進制數字。 對於上面的示例,0.001將變為0x0000(精確地表示0.001),123.456將變為0xF115(實際上表示123.425)並且100000將變為0xBEBF(實際表示為99975.169)。
看來你必須編寫自己的轉換例程。 ecvt
庫函數可能會有所幫助。
但我會簡單地用%.3g
或%.4g
格式,剝去多余的加號,並在指數前前導零和收工。 這主要留下一些可以優化的小數點。 既然你非常關心你的JSON響應的大小,你可能會使用HTTP壓縮,所以我懷疑這會帶來很多開銷。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.