簡體   English   中英

printf的最大數字信息密度

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM