Вопрос

Для простого проекта я должен сделать большие числа (например,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) обычно может быть дешево выполнено в конвейере integer на современных архитектурах с использованием таких инструкций, как CLZ или a немного крутящий хак, что дает число от 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);
}

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

Другие советы

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

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;

Не играйте с номером, вместо этого 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 /

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

Прежде всего, если вам нужно отформатировать ноль, вы не хотите брать логарифм этого. Во-вторых, вы хотите что-то красивое, поэтому не хотите, например, "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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top