Расчет округлого порядка величины - PullRequest
2 голосов
/ 30 июля 2009

Для простого проекта я должен сделать большие цифры (например, 4294967123) читаемыми, поэтому я пишу только первые цифры с префиксом (4294967123 -> 4.29G, 12345 -> 12.34K и т. Д.)

Код (упрощенный) выглядит так:

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

Это работает, но я думаю, что есть более элегантное / лучшее решение, чем вычисление логарифма с полной точностью, округление и приведение к целому числу снова.

Другие решения, о которых я подумал:

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

(это значительно медленнее, но легче для чтения)

Числа распределяются между в соответствии с законом Бенфорда, и число должно рассматриваться как 64-разрядное число без знака, так как не должно быть ошибки округления около 10 ^ x (например, в python math.log(1000,10) возвращает 2.999996, что переводится в 2). Есть ли какой-нибудь быстрый, точный способ пропустить меня?

Ответы [ 5 ]

17 голосов
/ 30 июля 2009

Ваш код log10 / floor отлично читается, и его производительность, скорее всего, будет меньше, чем при форматировании строки, которое вы впоследствии будете использовать для вывода.

Однако предположим, что вам действительно нужна производительность ...

Обратите внимание, что log10 (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);
}

Этот подход можно обобщить для нахождения логарифма любой базы, просто вычислите соответствующую константу, как описано выше.

2 голосов
/ 30 июля 2009

Это самый простой и простой метод, который я могу придумать ... и, возможно, он будет немного быстрее, чем вычисление логарифма:

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;
1 голос
/ 30 июля 2009

Не возитесь с номером, вместо этого s (n) печатает число в строка, использующая "% E", затем подставьте соответственно E + 00 E + 03 E + 09 (и т. д.) (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 будет что-то вроде

-e s / E + 0 // -e s / E + 3 / K / -e s / E + 6 / M / -e s / E + 9 / G /

0 голосов
/ 05 сентября 2009

Прежде всего, если вам нужно отформатировать ноль, вы не хотите брать логарифм этого. Во-вторых, вы хотите что-то красивое, поэтому вы не хотите, например, «1000M» за 999 800 000. В-третьих, вы, вероятно, хотите округлить.

Я предлагаю вам использовать что-то вроде этого псевдокода:


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
0 голосов
/ 30 июля 2009

Преобразуйте число в строку и используйте длину строки. Это, конечно, не быстрее, но будет очень точным. Затем вы можете продолжить и использовать строку непосредственно для построения результата, нарезав его соответствующим образом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...