Frage

Für ein einfaches Projekt habe ich eine große Zahl machen (z 4294967123) lesbar, so schreibe ich nur die ersten Ziffern mit einem Präfix (4294967123 -> 4,29 g, 12345 -.> 12.34K etc)

Der Code (vereinfacht) sieht wie folgt aus:

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

Es funktioniert, aber ich denke, dass es eine elegantere / bessere Lösung als die volle Präzision Logarithmus Berechnung, es Rundung und es nach unten in einen int wieder werfen.

Andere Lösungen dachte ich an:

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

(Dies ist deutlich langsamer, aber leichter zu lesen)

Die Zahlen verteilt sind zwischen nach Benford Gesetz und die Zahl ist als unsigned 64-Bit-Zahl behandelt werden, da es in der Nähe von 10 ^ x (zB in Python math.log(1000,10) kehrt 2,999996, was 2 platt wird) kein Rundungsfehler sein sollte, . Gibt es eine schnelle, präzise andere Art und Weise ich fehle?

War es hilfreich?

Lösung

Ihr log10 / Boden Code ist perfekt lesbar sind und Ihnen die Kosten werden wahrscheinlich durch die von der Zeichenfolge den Schatten gestellt werden Formatierungen, die Sie anschließend auf Ihrem Ausgang tun.

Allerdings: Angenommen, Sie auf waren wirklich müssen die Leistung ...

Man beachte, dass log10 (x) == log2 (x) / log2 (10) == log2 (x) * 1 / log2 (10)

1 / log2 (10) eine Konstante

log2 (x) kann in der Regel billig in der Integer-Pipeline auf modernen Architekturen durchgeführt werden Anweisungen wie CLZ oder ein Bit-twiddling-Hack , eine Zahl zwischen 0 und 63 für eine 64-Bit-Ganzzahl ergibt. Das paßt in 6 Bits, uns nach dem Radix Punkt auf 58 Bits verließ bis verwendbar für Festpunktarithmetik in einem 64-Bit-Typ.

So können wir dann mit Festkommaarithmetik die log10 zu finden:

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;
}

Die Implementierung von integer_log2 ist Compiler / plattformabhängig; z.B. auf GCC / PowerPC, es ist

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

Dieser Ansatz kann für die Suche nach dem Logarithmus jeder Basis verallgemeinert werden, einfach die entsprechende Konstante berechnen, wie oben beschrieben.

Andere Tipps

Dies ist die einfachste und einfache Methode i ... denken kann, und vielleicht wird es ein bisschen schneller sein als die Berechnung des Logarithmus:

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;

Sie nicht mit der Nummer Geige, statt s (n) printf die Zahl in eine string "% E" verwendet wird, dann ersetzen geeignet für E + 00 E + 03 E + 09 (Etc) (IIRC, sollten Sie nur Potenzen von 3 mit der wissenschaftlichen Schreibweise erhalten -. Das ist, was Sie wollen)

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) ist etwas chaotisch in C.

sed etwas wie

sein würde

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

Konvertieren Sie die Zahl in einen String und die Saiten Länge verwenden. Dies ist sicherlich nicht schneller, aber wird sehr genau. Sie können dann weitergehen und die Zeichenfolge verwenden, um direkt das Ergebnis zu bauen, indem sie in geeigneter Weise schneiden.

Zunächst einmal sollten Sie eine Null zu formatieren müssen, wollen Sie nicht den Logarithmus davon zu nehmen. Zweitens wollen Sie etwas hübsch, so dass Sie nicht wollen, zum Beispiel „1000M“ für 999.800.000. Drittens wollen Sie wahrscheinlich Rundung.

Ich schlage vor, dass Sie so etwas wie dieser Pseudo-Code verwenden:


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
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top