سؤال

لمشروع بسيط لقد جعل أعداد كبيرة (على سبيل المثال 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 ^ س (على سبيل المثال في بيثون math.log(1000,10) يعود 2.999996، الذي يحصل على طوابق ل2) . هل هناك أي طريقة سريعة ودقيقة أخرى أنا في عداد المفقودين؟

هل كانت مفيدة؟

المحلول

وكود LOG10 / الطابق الخاص بك هو مقروء تماما، ومن المرجح أن تتضاءل تكلفة أداء من قبل أن السلسلة التنسيق الذي سيتم في وقت لاحق أن تفعل في الإخراج.

ولكن، لنفترض انك كنت ل<م> حقا بحاجة إلى أداء ...

لاحظ أن LOG10 (خ) == log2 (س) / log2 (10) == log2 (خ) * 1 / log2 (10)

1 / log2 (10) هو ثابت

وlog2 (خ) وعادة ما يمكن أن يؤديها بثمن بخس في خط أنابيب صحيح على أبنية الحديثة باستخدام إرشادات مثل CLZ أو <وأ href = "http://graphics.stanford.edu/~seander/bithacks.html" يختلط = "noreferrer"> قليلا twiddling الإختراق و يحقق رقما بين 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 غير مترجم / منبر التي تعتمد على. مثلا على دول مجلس التعاون الخليجي / باور، انها

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;

ولا كمان مع عدد، بدلا ق (ن) printf عدد في السلسلة باستخدام "٪ E"، ثم بديلا بشكل مناسب لE + 00 + 03 E 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.

والحوار الاقتصادي الاستراتيجي سيكون شيء من هذا القبيل

و-e ق / E + 0 // ق -e / E + 3 / K / ثانية -e / E + 6 / M / -e ق / E + 9 / G /

وتحويل عدد إلى سلسلة واستخدام طول السلاسل. هذا هو بالتأكيد ليست أسرع، ولكن سوف تكون دقيقة جدا. يمكنك بعد ذلك على المضي قدما واستخدام السلسلة مباشرة لبناء نتيجة تشريح بشكل مناسب من قبل.

وأولا وقبل كل شيء، يجب أن تحتاج إلى تنسيق الصفر، وكنت لا تريد أن تتخذ لوغاريتم ذلك. ثانيا، كنت تريد شيئا جدا، لذلك كنت لا تريد، على سبيل المثال، "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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top