문제

간단한 프로젝트의 경우 많은 수를 만들어야 합니다(예: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 근처에서 반올림 오류가 없어야 하기 때문입니다(예:파이썬으로 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 또는 약간의 장난 해킹, 64비트 정수에 대해 0에서 63 사이의 숫자를 생성합니다.이는 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);
}

이 접근 방식은 모든 밑의 로그를 찾기 위해 일반화될 수 있습니다. 위에서 설명한 대로 적절한 상수를 계산하기만 하면 됩니다.

다른 팁

이것은 내가 생각할 수있는 가장 간단하고 간단한 방법입니다. 그리고 아마도 로그를 계산하는 것보다 조금 더 빠를 것입니다.

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;

"%e"를 사용하여 숫자를 문자열로 인쇄 한 다음 숫자로 숫자를 사용한 다음 e+00 e+03 e+09 (etc)를 적절하게 대체합니다 (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는 같은 것입니다

-es/e+0 // -es/e+3/k/-es/e+6/m/-es/e+9/g/

숫자를 문자열로 변환하고 문자열 길이를 사용하십시오. 이것은 확실히 빠르지 않지만 매우 정확합니다. 그런 다음 계속해서 문자열을 직접 사용하여 결과를 적절하게 슬라이싱하여 결과를 빌드 할 수 있습니다.

우선, 0을 제로 형식해야한다면 그 대수를 받고 싶지 않습니다. 둘째, 당신은 예쁜 것을 원하기 때문에 예를 들어 999,800,000의 "1000m"을 원하지 않습니다. 셋째, 당신은 아마 반올림을 원할 것입니다.

이 의사 코드와 같은 것을 사용하는 것이 좋습니다.


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