计算规模的一轮订单(Calculating a round order of magnitude)

2019-09-21 10:12发布

对于一个简单的项目,我必须做出大的数字(例如4294967123)可读的,所以我有一个前缀只写了第一个数字(4294967123 - > 4.29G,12345 - > 12.34K等)

代码(简化)如下:

const char* postfixes=" KMGT";
char postfix(unsigned int x)
{
     return postfixes[(int) floor(log10(x))];
}

它的工作原理,但我认为还有比计算全精度数,舍入并重新铸造它归结为一个int一个更优雅/更好的解决方案。

其他的解决方案我认为:

int i=0;
for(; x >= 1000 ; ++i) x/=1000;
return postfixes[i];

(这是显著慢,但更易于阅读)

这些数字是根据本福德定律和数量之间分布应被视为无符号的64比特数,因为应接近10没有舍入误差^ x的(例如在python math.log(1000,10)返回2.999996,它得到向下舍入至2)。 有我缺少任何快速,准确的其他办法吗?

Answer 1:

你的日志10 /地板代码是完全可读的,其性能成本将有可能通过该字符串的格式化您以后将在您的输出做相形见绌。

但是,假设你是真正需要的性能...

注意,日志10(x)的== LOG2(X)/ LOG2(10)== LOG2(X)* 1 / LOG2(10)

1 / LOG2(10)是一个常数

LOG2(X)通常可以廉价地在整数流水线上使用诸如CLZ或指令现代架构执行位操作劈 ,产生0和63之间的数字为64位整数。 适合在6位,使我们高达58个比特可用于在64位类型的固定点算术小数点后。

因此,我们可以再使用定点运算找到LOG10:

unsigned long long integer_log10( unsigned long long _in )
{
    unsigned long long log10fp6x58 = 0x134413509f79ff0llu; // (unsigned long long) (double(1llu<<58) / log2(10.0))
    return (((integer_log2(_in)) * log10fp6x58)+(1llu<<57)) >> 58;
}

integer_log2的实现是编译器/平台的依赖; 如对GCC / PowerPC的,这是

unsigned long long integer_log2( unsigned long long _in )
{
    return 63 - __cntlzd(_in);
}

这种方法可以被概括为找到任何碱的对数,简单地计算适当的常数,如上所述。



Answer 2:

这是最直接,最简单的方法我能想到的...也许它会比计算更快的对数了一下:

postfixes = {{1e12, "T"},
             {1e9,  "G"},
             {1e6,  "M"},
             {1e3,  "K"}}

for each postfix in postfixes{
    if(x > postfix.value){
        return (x / postfix.value) + postfix.letter;
    }
}

return x;


Answer 3:

不要用数字不甘示弱,而不是S(N)printf的数量为使用 “%E” 的字符串,然后替换为适当的E + 00 E + 03 E + 09(ETC)(IIRC,你应该只得到权力3科学记数法 - 这是你想要的)。

char number_buff[30];
snprintf(number_buff, 29, "%E", x);
char *powered_number_string = substitute_powers(number_buff);

char *substitute_powers(const char *number_buff)是C.凌乱

SED会是这样的

-es / E + 0 // -es / 3 + E / K / -es / E + 6 / M / -es / E + 9 / G /



Answer 4:

转换为数字转换为字符串,并使用字符串长度。 这当然不是更快,但会是非常准确的。 然后,您可以去和直接使用字符串通过适当切片它构建的结​​果。



Answer 5:

首先,你应该需要格式化一个零,你不想服用的量的对数。 其次,你要的东西很漂亮,所以你不想要的,例如,“1000M”为999800000。 第三,你可能需要四舍五入。

我建议你使用这样的伪代码:


function format(long x by value)
int p=5, char suf
if x<100000 then return string(x)
if x>=10000000000000 then
   x/=100000000
   p+=8
if x>=1000000000 then
   x/=10000
   p+=4
if x>=10000000 then
   x/=100
   p+=2
if x>=1000000 then
   x/=10
   p+=1
x+=5
if x>=100000 then
   x/=10
   p+=1
switch(p/3)
   6: suf='E'
   5: suf='P'
   4: suf='T'
   3: suf='G'
   2: suf='M'
   1: suf='K'
switch(p mod 3)
   2: return format("000 A",x/1000,suf)
   1: return format("00.0 A",x/10000,(x%10000)/100,suf)
   0: return format("0.00 A",x/100000,(x%100000)/100,suf)
end function


文章来源: Calculating a round order of magnitude