对于一个简单的项目,我必须使大数字(例如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).我有什么快速,准确的其他方式吗?
您的log10/floor代码是完全可读的,其性能成本可能会与您随后在输出上执行的字符串格式相比相形见绌.
但是,假设你真的需要表现......
注意log10(x)== log2(x)/ log2(10)== log2(x)*1/log2(10)
1/log2(10)是常数
log2(x)通常可以在现代体系结构的整数流水线中使用诸如CLZ之类的指令或者一个小错误的hack来廉价地执行,从而产生一个介于0和63之间的数字,用于64位整数.这适用于6位,在64位类型的基点可用于定点运算之后,我们最多可达58位.
那么我们就可以使用定点算法来找到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); }
这种方法可以推广用于找到任何碱基的对数,简单地计算如上所述的适当常数.