Soru Bir tam sayıya kaç bayta ihtiyaç duyulduğunu nasıl belirleyebilirim?


Kesinlik kaybetmeden bir tamsayı depolamak için gereken minimum bayt sayısını hesaplamanın en etkili yolunu arıyorum.

e.g.

int: 10 = 1 byte
int: 257 = 2 bytes;
int: 18446744073709551615 (UINT64_MAX) = 8 bytes;

Teşekkürler

Not; Bu milyonlarca kez çağrılacak bir karma fonksiyonları içindir.

Ayrıca bayt boyutlarının iki kişilik bir gücü olmak zorunda değildir.

En hızlı çözüm, tronics cevabına dayalı olarak görünüyor:

    int bytes;
    if (hash <= UINT32_MAX) 
    {
        if (hash < 16777216U)
        {
            if (hash <= UINT16_MAX)
            {
                if (hash <= UINT8_MAX) bytes = 1;
                else bytes = 2;
            }
            else bytes = 3;
        }
        else bytes = 4;
    } 
    else if (hash <= UINT64_MAX) 
    {
        if (hash < 72057594000000000ULL) 
        {
            if (hash < 281474976710656ULL) 
            {
                if (hash < 1099511627776ULL) bytes = 5;
                else bytes = 6;
            }
            else bytes = 7;
        }
        else bytes = 8;
    }

Çoğunlukla 56 bit vals kullanarak hız farkı Thomas Pornin cevabı ile karşılaştırıldığında minimal (ama ölçülebilir) idi. Ayrıca, karşılaştırılabilir olan __builtin_clzl kullanarak çözümü test etmedim.


25
2018-02-16 16:26


Menşei


Şüphelenirim __builtin_clzll size herhangi bir dallanma olmaksızın 3 montaj talimatı gibi bir şey olmasından dolayı size üstün performans verecektir. Zorunlu kontrolü sıfıra indirdikten sonra bile, yaklaşık 10 talimatla sonuçlanırsınız. - D.Shawley
Tamam, 10,000,000 aramanın 10 iterasyonunun üzerinde __builtin_clzll test ettim, ortalama 0.006s daha yavaştı. Minimal fark, ama derleyiciye bağımlı olduğu için Tronics cevabı hala en iyisi. - Ben Reeves
Şaşırtıcı bir şekilde farklı cevap kümeleri için +1 soru ortaya çıktı. - Richard Szalay


Cevaplar:


Sadece iki basite ihtiyacın var ifSadece ortak boyutlarla ilgileniyorsanız. Bunu düşünün (aslında imzasız değerleriniz olduğunu varsayarak):

if (val < 0x10000) {
    if (val < 0x100) // 8 bit
    else // 16 bit
} else {
    if (val < 0x100000000L) // 32 bit
    else // 64 bit
}

Diğer boyutlar için test etmeniz, bir orta noktayı seçmeniz ve daha sonra iç içe testler yapmanız gerekiyorsa, test sayısını her durumda çok düşük tutacaktır. Bununla birlikte, bu durumda, testin bir özyinelemeli fonksiyonunun yapılması, kodu basit tutmak için daha iyi bir seçenek olabilir. İyi bir derleyici, özyinelemeli çağrıları en iyi duruma getirecek ve sonuçta ortaya çıkan kod hala hızlı olacaktır.


20
2018-02-16 16:30



Basit, hızlı çözüm için +1 - Xorlev
Ama ne zaman 256 bit tam sayılarımız var? ;) - Earlz
Şubenin yanlış tahmin cezasını dikkate alırsanız hızlı olmaz - ZelluX
Bu cevap için +1. Bir günlük kullanmak kadar güzel olmayabilir ama işi çok daha hızlı yapıyor. - Spencer Ruport
Bu, rasgele bir bayt sayısına ihtiyaç duyuyorsa çok kullanışlı olmayacaktır. - Peter Alexander


Bunu kullan:

int n = 0;
while (x != 0) {
    x >>= 8;
    n ++;
}

Bu varsayar x (pozitif) değerinizi içerir.

Sıfırın, hiçbir bayt olarak kodlanamayacağını bildirmesi gerektiğini unutmayın. Ayrıca, çoğu değişken boyuttaki kodlamanın bir dosya veya akışta kodlamanın nerede durduğunu bilmesi için bazı uzunluk alanına veya sonlandırmaya gereksinimi vardır (genellikle, tamsayı ve boyut hakkında bir zihin kodladığınızda, kodlanmış nesnenizde birden fazla tam sayı vardır).


28
2018-02-16 16:36



Bit kaydırma için +1 - Xorlev
Basit bir çözüm için +1, bu mümkün olduğunca hızlı olmayabilir (ancak küçük değerler için çok hızlıdır, dolayısıyla genel olarak en hızlı çözüm olabilir). - Tronic
@Tronic: bu çözümün, dikotomik aramanızdan daha hızlı olup olmadığı, giriş verilerinin modellerine bağlıdır. Gerçekten ölçülebilir bir performans farkı sergilemek için çok özel bir kurulum yapılacağını düşünüyorum. Kodum, "daha uzun tipler" ile otomatik olarak ilgilenmenin küçük bir avantajına sahiptir (örneğin, 128 bit tipli yeni C derleyiciler geliştirildiğinde hiçbir şeyi değiştirmeye gerek yoktur). - Thomas Pornin
Kullanabilirsin int n = 0; do { x >>= 8; n++; } while(x); 0 yerine 1 bayt döndürmek isterseniz. - Chris Lutz


Bir bayt varsayarsak, x bitini temsil etmek için [log2 (x) / 8] + 1 bayt gerekir [x] = kat (x).

Tamam, şimdi bayt boyutlarının mutlaka ikisinin gücü olmadığını görüyorum. Bayt boyutlarını düşünün b. Formül hala [log2 (x) / b] + 1'dir.

Şimdi, kütüğü hesaplamak için, arama tablolarını (en iyi şekilde hızlandırılmış) kullanın veya tamsayılar için de çok hızlı olan ikili aramayı kullanın.


9
2018-02-16 16:30



Bu iyi bir yol. - Justin Peel
Evet, ancak günlüğü hesaplamak diğer yöntemlere göre çok yavaş olacaktır. - SLaks
log2, kayan nokta işlevidir ve bu nedenle hatalı yuvarlama hatalarına neden olur. Ayrıca, bu çözümün benimkinden çok daha yavaş olduğunu tahmin edebilirdim (ama ben kıyaslama yapmadım, o yüzden bunu bir tuz tohumu ile al). - Tronic
Doğru, bunu olduğu gibi uygulamamalısın. Bu sadece formüllerdir, büyük olasılıkla günlükleri ikili arama ve bitsel işlemlerle veya bir çeşit arama tablosundan hesaplamak isteyebilirsiniz. Tronik: doğru, gönderdiğiniz şey gibi bir şey daha hızlı olurdu, ancak daha fazla koşula ihtiyacı var. - IVlad
@Slaks: zorunlu olarak değil: verimli arama tablolarında tutulabilir ... - Mitch Wheat


İlk olarak log2 (N) ile aynı olan en yüksek bit kümesini elde edebilir ve sonra da tavanın ihtiyaç duyduğu baytları alabilirsiniz (log2 (N) / 8).

Burada kopyalanan en yüksek bit kümesinin konumunu almak için bazı küçük kesiciler var. http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObviousve bu algoritmaların nasıl çalıştığının ayrıntıları için URL'yi tıklayabilirsiniz.

64 bit IEEE float ile bir tamsayı tamsayı günlüğü 2'yi bulun.

int v; // 32-bit integer to find the log base 2 of
int r; // result of log_2(v) goes here
union { unsigned int u[2]; double d; } t; // temp

t.u[__FLOAT_WORD_ORDER==LITTLE_ENDIAN] = 0x43300000;
t.u[__FLOAT_WORD_ORDER!=LITTLE_ENDIAN] = v;
t.d -= 4503599627370496.0;
r = (t.u[__FLOAT_WORD_ORDER==LITTLE_ENDIAN] >> 20) - 0x3FF;

Bir arama tablosuna sahip bir tamsayı günlüğü 2'yi bulun.

static const char LogTable256[256] = 
{
#define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
    -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
    LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6),
    LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7)
};

unsigned int v; // 32-bit word to find the log of
unsigned r;     // r will be lg(v)
register unsigned int t, tt; // temporaries

if (tt = v >> 16)
{
  r = (t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt];
}
else 
{
  r = (t = v >> 8) ? 8 + LogTable256[t] : LogTable256[v];
}

O (lg (N)) işlemlerinde bir N bitlik tamsayı tabanını 2 bulun.

unsigned int v;  // 32-bit value to find the log2 of 
const unsigned int b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000};
const unsigned int S[] = {1, 2, 4, 8, 16};
int i;

register unsigned int r = 0; // result of log2(v) will go here
for (i = 4; i >= 0; i--) // unroll for speed...
{
  if (v & b[i])
  {
    v >>= S[i];
    r |= S[i];
  } 
}


// OR (IF YOUR CPU BRANCHES SLOWLY):

unsigned int v;          // 32-bit value to find the log2 of 
register unsigned int r; // result of log2(v) will go here
register unsigned int shift;

r =     (v > 0xFFFF) << 4; v >>= r;
shift = (v > 0xFF  ) << 3; v >>= shift; r |= shift;
shift = (v > 0xF   ) << 2; v >>= shift; r |= shift;
shift = (v > 0x3   ) << 1; v >>= shift; r |= shift;
                                        r |= (v >> 1);


// OR (IF YOU KNOW v IS A POWER OF 2):

unsigned int v;  // 32-bit value to find the log2 of 
static const unsigned int b[] = {0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0, 
                                 0xFF00FF00, 0xFFFF0000};
register unsigned int r = (v & b[0]) != 0;
for (i = 4; i > 0; i--) // unroll for speed...
{
  r |= ((v & b[i]) != 0) << i;
}

9
2018-02-16 16:38



İlginç bağlantı için +1 - pythonic metaphor
Ceil (Log256 (N)) daha da basittir. - IamIC
Ciel (log2 (N + 1) / 8) daha doğrudur. 256 takın:> 1 değil, 1 olsun. - Mad Physicist
Başlangıçta log yaklaşımından bahsetmiştim. Ancak performans bilindikçe, bit işlemlerine göre log () değerini hesaplamak çok daha karmaşıktır. - ZelluX


İlk '1' bitin konumunu en anlamlı olandan bulma fonksiyonu (clz veya bsr) genellikle basit bir CPU talimatıdır.2), böylece 8 byte gereken bayt sayısını elde etmek için bölebilirsiniz. Gcc'de, orada __builtin_clz bu görev için:

#include <limits.h>
int bytes_needed(unsigned long long x) {
   int bits_needed = sizeof(x)*CHAR_BIT - __builtin_clzll(x);
   if (bits_needed == 0)
      return 1;
   else
      return (bits_needed + 7) / 8;
}

(MSVC'de _BitScanReverse gerçek.)


8
2018-02-16 16:59





Günlüğü alarak bit sayısını bulun2 sayının daha sonra bayt sayısını elde etmek için 8'e bölerek.

Günlüğü bulabilirsinizn formülü ile x

kütükn(x) = günlük (x) / log (n)

Güncelleştirme:

Bunu gerçekten hızlı bir şekilde yapmanız gerektiğinden, Bit Twiddling Hacks hızlı bir şekilde hesaplamak için çeşitli yöntemler vardır2(X) tanımlanmaktadır. Look-up tablo yaklaşımı ihtiyaçlarınızı karşılayacak gibi görünüyor.


5
2018-02-16 16:32





Bu size bayt sayısını verecektir. Bu kesinlikle en verimli değil, ama bir kırmızı kan hücresinde bulunan enerji ile çalışan bir nanobot programlamıyorsanız, önemli değil.

int count = 0;
while (numbertotest > 0)
{
  numbertotest >>= 8;
  count++;
}

4
2018-02-16 16:30



Nanobot buna katılmazdı. - Geo