printf的最大数字信息密度

时间:2014-11-02 13:16:49

标签: c json number-formatting

我的用例是将数字写入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%.4gprintf("%.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 比较产生code here) :

{{1}}

2 个答案:

答案 0 :(得分:1)

用于将特定范围内的数字打包成最小的无符号整数:

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

之类的东西

这样,您可以将数字压缩到4个十六进制数字。对于上面的示例,0.001将变为0x0000(精确表示0.001),123.456将变为0xF115(实际表示123.425),100000将变为0xBEBF(实际表示为99975.169)。

答案 1 :(得分:1)

您似乎必须编写自己的转换例程。 ecvt库函数可能会有所帮助。

但我只是使用%.3g%.4g格式,在指数之前删除多余的加号和前导零,并将其称为一天。这主要留下一些可以优化的小数点。既然你非常关心你的JSON响应的大小,你可能会使用HTTP压缩,所以我怀疑这会导致很多开销。