문제
간단한 프로젝트의 경우 많은 수를 만들어야 합니다(예: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