Bir tamsayıdaki basamak sayısını belirlemenin etkili yolu


145

C ++ 'da bir tamsayıda kaç basamak bulunduğunu belirlemenin çok etkili bir yolu nedir ?


11
Hangi bazda? 2? 10?
Jacob Krall

2
Bunu üs 10'da yapmak istiyorum
Seth

1
Bir keresinde ilgili bir soru sordum: Bir int'deki ilk haneyi nasıl alabilirsin? Aşağıdaki metodolojilerin çoğu insanların cevaplarında kullanılmıştır. Görevinizle ilgili olması durumunda bağlantı
Dinah

Satır içi montaj uygun mudur?
György Andrasek

1
Tüm bu cevaplar temel 10 açısından olsa da, istenen herhangi bir temel için sonucu hesaplamak oldukça kolaydır.
Ira Baxter

Yanıtlar:


106

Tamsayı boyutunu bildiğinizi varsayarsak, en etkili yol bir arama olacaktır. Çok daha kısa logaritma tabanlı yaklaşımdan daha hızlı olmalıdır. '-' saymayı önemsemiyorsanız + 1'i kaldırın.

// generic solution
template <class T>
int numDigits(T number)
{
    int digits = 0;
    if (number < 0) digits = 1; // remove this line if '-' counts as a digit
    while (number) {
        number /= 10;
        digits++;
    }
    return digits;
}

// partial specialization optimization for 32-bit numbers
template<>
int numDigits(int32_t x)
{
    if (x == MIN_INT) return 10 + 1;
    if (x < 0) return numDigits(-x) + 1;

    if (x >= 10000) {
        if (x >= 10000000) {
            if (x >= 100000000) {
                if (x >= 1000000000)
                    return 10;
                return 9;
            }
            return 8;
        }
        if (x >= 100000) {
            if (x >= 1000000)
                return 7;
            return 6;
        }
        return 5;
    }
    if (x >= 100) {
        if (x >= 1000)
            return 4;
        return 3;
    }
    if (x >= 10)
        return 2;
    return 1;
}

// partial-specialization optimization for 8-bit numbers
template <>
int numDigits(char n)
{
    // if you have the time, replace this with a static initialization to avoid
    // the initial overhead & unnecessary branch
    static char x[256] = {0};
    if (x[0] == 0) {
        for (char c = 1; c != 0; c++)
            x[c] = numDigits((int32_t)c);
        x[0] = 1;
    }
    return x[n];
}

5
Muhtemelen cevabımdan daha hızlı, aferin. Ek verimlilik için, giriş sayılarınızın çoğunlukla küçük olacağını biliyorsanız (100.000'den az tahmin ediyorum), testleri tersine çevirin: (x <10) 1 döndürürse; (x <100) 2 ise; Böylece işlev daha az test yapar ve daha hızlı çıkar.
squelart

29
Veya doğrusal ifadeler yerine ikili bir arama yapmak için if ifadelerini yeniden sıralayın ve iç içe geçirin.
dave4420

1
Bu iyi bir fikir değil. Mimari 256 bit tamsayılara genişlediğinde ne olur? Geri dönüp bu kodu değiştirmeyi unutmayın. Gerçek hayatta olmayacak ve sice bu muhtemelen daha büyük mimarlar üzerinde çalışma sorunları üzerinde kendinizi her türlü tampon açacağınız doğru boyutta bir tampon oluşturmak için kullanılacaktır.
Martin York

3
Düzgün bir sayı dağılımı varsayarsak, ters doğrusal arama (maksimum basamaktan 1'e kadar) ortalama olarak ikili aramaya göre daha hızlı olabilir, çünkü N basamaklı N-1 basamaklı graphics.stanford.edu/~ '
fa.

6
256 veya 128 bit tamsayılar için çok endişelenmem. Evrendeki elektron sayısını saymanız gerekmedikçe (son yaptığımda 10 ^ 78), 64 bit oldukça iyi sonuç verecektir. 32 bitlik makineler ~ 15 yıl sürdü. 64 bit makinelerin çok daha uzun süreceğini tahmin ediyorum. Daha büyük sayı için, çok duyarlıklı aritmetik iyi olacak ve hesaplama basamak sayısı verimliliğinin önemli olup olmadığından şüphe ediyorum.
Ira Baxter

74

En basit yol:

unsigned GetNumberOfDigits (unsigned i)
{
    return i > 0 ? (int) log10 ((double) i) + 1 : 1;
}

log10 <cmath>veya içinde tanımlanır <math.h>. Burada yayınlanan diğerlerinden daha hızlı olup olmadığını görmek için bunu profillemeniz gerekir. Kayan nokta hassasiyeti açısından bunun ne kadar sağlam olduğundan emin değilim. Ayrıca, argüman negatif değerler olarak imzalanmamıştır ve günlük gerçekten karışmaz.


7
32 bit ints ve 56 bit float için bu muhtemelen işe yarar. Giriş uzunsa (64 bit), 56 bitlik çift kesinlikli günlük, 10 ^ n büyük değerlerine yakın değerlerde yanlış yanıt vermesine neden olabilir. 2 ^ 50 üzerinde sorun bekliyoruz.
Ira Baxter

1
Günlük işlevlerinin ne kadar doğru olduğu sorusu da vardır. Modern kütüphanelerde ne kadar doğru olduklarını kontrol etmedim ve milyarda bir parça için iyi olmalarına körü körüne güvenmekten rahat olmazdım.
David Thornley

@DavidThornley: derleyici komut satırında belirtilmedikçe günlük veya diğer matematik işlevleri mükemmel bir şekilde kesindir. bazıları derleme zamanında x86 özüne dönüştürülecektir. bazıları mevcut değildir ve mevcut içsel formüllere genişleyecektir. örneğin, -fpfastx87 yerine SSE instrinsics kullanımını görebiliyorsanız, bu durum IIRC hassasiyetinde daha az garanti sağlar. ama varsayılan olarak sorun yok.
v.oddou

@DavidThornley: Kesinlikten daha fazlası. Soru, ilgili tüm k için log10 (10 ^ k) ≥ k değerinin garanti edilip edilmediğidir. Yani herhangi bir kaçınılmaz yuvarlama hatasının doğru yönde olması garanti edilir. Sonuç olarak k + eps çalışıyor, k - eps çalışmıyor. Ve "Mükemmel derecede hassas" naif.
gnasher729

1
İ> 0 testi
Pat

60

Belki de soruyu yanlış anladım ama bunu yapmıyor mu?

int NumDigits(int x)  
{  
    x = abs(x);  
    return (x < 10 ? 1 :   
        (x < 100 ? 2 :   
        (x < 1000 ? 3 :   
        (x < 10000 ? 4 :   
        (x < 100000 ? 5 :   
        (x < 1000000 ? 6 :   
        (x < 10000000 ? 7 :  
        (x < 100000000 ? 8 :  
        (x < 1000000000 ? 9 :  
        10)))))))));  
}  

29
Ve bu çözüm en hızlı olacaksa şaşırmam.
VisioN

32
int digits = 0; while (number != 0) { number /= 10; digits++; }

Not: "0", 0 haneye sahip olacak! 1 basamaklı görünmek için 0'a ihtiyacınız varsa, şunu kullanın:

int digits = 0; do { number /= 10; digits++; } while (number != 0);

(Teşekkürler Kevin Fegan)

Sonunda, buradaki tüm cevapların hangisinin makinenizde daha hızlı olacağını bilmek için bir profil oluşturucu kullanın ...


3
Bu benim sürdüğüm kaydedilmemiş döngü yaklaşımından daha hızlı olabilir veya olmayabilir - farkı profillemeniz gerekir (uzun vadede ihmal edilebilir olmalıdır).
Vitali

Kesinlikle, profilleme gerçekten kesin olarak bilmenin tek yoludur! Ben S'nin tavanı (log10 ()) cevabı kaybolduğundan cevabımı bu yorumla güncelledim.
squelart

11

Uygulama şaka: Bu en etkili yolu (basamak sayısı derleme zamanında hesaplanır):

template <unsigned long long N, size_t base=10>
struct numberlength
{
    enum { value = 1 + numberlength<N/base, base>::value };
};

template <size_t base>
struct numberlength<0, base>
{
    enum { value = 0 };
};

Biçimlendirme, giriş öğeleri vb. İçin sayı alanı için gereken genişliği belirlemekte yararlı olabilir.


4
İlk olarak, çözümünüz 0 için çalışmaz. İkincisi, çözümünüz bir değişkenin genel durumu için uygulanamaz. Üçüncüsü, sabit bir değişmezi kullanıyorsanız, kaç basamak olduğunu zaten biliyorsunuzdur.
Vitali

0 için de çalışır. Ayrıca herhangi bir baz için de çalışır. Geri kalanı, daha önce özetlediğim geçerli noktalar.
blinnov.com

3
Aslında öyle olduğunu sanmıyorum. Başarısız olur 0ve ayrıca bazda başarısız olur 1:) ve baz olarak verilirse sıfır hata ile bölünür 0. Gerçi düzeltilebilir. Her neyse, çok eski bir yazıya atıyorum, çok üzgünüm, sadece bunun bir şaka olması gerektiğini ve aslında yararlı olabileceğini düşünüyorum.
tjm

9

Kabul ettiğiniz yanıtın çok daha kısa bir sürümü için Bit Twiddling Hacks'e bakın . Ayrıca, girdiniz normal olarak dağıtılırsa, önce büyük sabitleri kontrol ederek cevabı daha erken bulma avantajına sahiptir. (v >= 1000000000)değerlerin% 76'sını yakalar, bu nedenle ilk önce ortalama olarak daha hızlı olacağını kontrol edin.


Biraz döndürmenin aslında daha hızlı olup olmadığı belirsiz. En kötü durumda bile, değiştirilmiş yaklaşımım 4 karşılaştırma gerektirir (bölümleme işlemini daha ayrıntılı incelemişsem, bu olası görünmese de) 3'e düşürebilir). Bunun aritmetik işlemler + bellek yükleri tarafından yenileceğinden şüpheleniyorum (yeterince erişilse bile, bunlar CPU önbelleğinde kayboluyor). Unutmayın, verdikleri örnekte, günlük tabanını (2) bazı soyut IntegerLogBase2 işlevi olarak gizlerler (ki bu aslında ucuz değildir).
Vitali

Bir takip olarak, evet eğer numaralar normal olarak dağıtılırsa, sırayla kontrol yapmak daha hızlıdır. Bununla birlikte, en kötü durumda dejenere olmanın iki kat daha yavaş olma durumu vardır. Girdi alanı yerine rakam sayısına göre bölümlere ayrılmış yaklaşım, davranışın dejenere bir vakası olmadığı ve her zaman en iyi performansı gösterdiği anlamına gelir. Ayrıca, sayıların eşit olarak dağıtılacağı varsayımını yaptığınızı unutmayın. Aslında, tahminimde <a href=" en.wikipedia.org/wiki/…> ile ilgili bazı dağıtımları takip etme olasılıkları daha yüksektir
Vitali

Biraz çevirme kesmek, yukarıdaki bölümleme yönteminden daha hızlı değildir, ancak burada bir şamandıra gibi daha genel bir durumunuz varsa potansiyel olarak ilginçtirler.
Corwin Joy

1
Biraz kıvranan kesmek, int log2 verildiğinde int log10'u almanın bir yolunu önerir. İnt log2'yi almanın çeşitli yollarını önerir, çoğunlukla birkaç karşılaştırma / dal içerir. (Sanırım öngörülemeyen dalların maliyetini küçümsüyorsun, Vitali). Satır içi x86 asm kullanabiliyorsanız, BSR komutu size bir değerin int log2 değerini verecektir (yani en önemli ayarlanmış bitin bit dizini). K8'de (10 döngü gecikmesi) biraz yavaş, ancak Core 2'de (2 veya 3 döngü gecikmesi) hızlı. K8'de bile, karşılaştırmalardan daha hızlı olabilir.
Peter Cordes

K10'da, lzcnt önde gelen sıfırları sayar, bu yüzden neredeyse bsr ile aynıdır, ancak 0 girişi artık tanımlanmamış sonuçlarla özel bir durum değildir. Gecikmeler: BSR: 4, LZCNT: 2.
Peter Cordes

8

dizeye dönüştürün ve sonra yerleşik işlevleri kullanın

unsigned int i;
cout<< to_string(i).length()<<endl;

7
int x = 1000;
int numberOfDigits = x ? static_cast<int>(log10(abs(x))) + 1 : 1;

3
Bu LOC'lar açısından etkili olsa da, kabul edilen cevapta belirtildiği gibi günlük kullanımı muhtemelen en iyi performansı vermeyecektir.
Ian

@Ian Neden olmasın? Sadece birkaç FPU talimatý. Diğer cevaplardaki tüm dallardan ve döngülerden daha iyi mil.
Lorne Marquis

5

Önceki bir poster, 10'a bölen bir döngü önerdi. Modern makinelerde çarpmalar çok daha hızlı olduğundan, aşağıdaki kodu tavsiye ederim:

 int digits = 1, pten=10; while ( pten <= number ) { digits++; pten*=10; }

1
şeytan ayrıntılarda - std :: numeric_limits <int> :: max == sayı ile ne olur - sonlandırmada bir sorun olabilir
pgast

2
Bu durumdan endişe ediyorsanız, çok büyük değerleri işlemek için bir IF daha ekleyebilirsiniz.
Ira Baxter

2
X86 makinelerinde, bu durumda kullanılan sabit 10 ile çarpma işleminin derleyici tarafından LEA R2, [8 * R1 + R1], ADD R1, R2 olarak uygulanabileceğini gözlemlemeliyim, bu yüzden maksimum 2 saat sürer. Değişkenlere göre çarpanlar onlarca saat alır ve bölünmeler çok daha kötüdür.
Ira Baxter

bölme yaklaşımının avantajı, negatif sayılar için endişelenmenize gerek olmamasıdır.
Johannes Schaub - litb

1
Bölme yaklaşımına göre çarpma yaklaşımını (işaret konusunu kaldırmak için bir fab ile) karşılaştırdım. Makinemde bölme yaklaşımı faktör 2 çarpma yaklaşımından daha yavaştır. Bunun erken optimizasyon olup olmadığı gerçekten nereye ve nasıl adlandırıldığına bağlıdır.
Spacemoose

5

Ppc mimarisinin bir bit sayma talimatı vardır. Bununla, pozitif bir tamsayının günlük tabanını 2 tek bir komutta belirleyebilirsiniz. Örneğin, 32 bit:

#define log_2_32_ppc(x) (31-__cntlzw(x))

Büyük değerlerde küçük bir hata payını kaldırabiliyorsanız, bunu birkaç temel komutla 10 günlük tabanına dönüştürebilirsiniz:

#define log_10_estimate_32_ppc(x) (9-(((__cntlzw(x)*1233)+1545)>>12))

Bu platforma özgüdür ve biraz yanlıştır, ancak aynı zamanda dal, bölüm veya kayan noktaya dönüşüm içermez. Her şey neye ihtiyacınız olduğuna bağlıdır.

Sadece ppc talimatlarını elden biliyorum, ancak diğer mimarilerde benzer talimatlar olmalı.


Bu çözüm log2 (15) = 4 bit ve log2 (9) = 4 bit hesaplar. Ancak 15 ve 9, yazdırmak için farklı sayıda ondalık basamak gerektirir. Bu nedenle, sayılarınızın bazen çok fazla basamakla yazdırılmasını önemsemediğiniz sürece işe yaramaz. Ancak bu durumda, int için her zaman "10" u seçebilirsiniz.
Ira Baxter

Vay canına, yaklaşık bir fonksiyon. Güzel.
2013

4
 #include <iostream>
 #include <math.h>

 using namespace std;

 int main()
 {
     double num;
     int result;
     cout<<"Enter a number to find the number of digits,  not including decimal places: ";
     cin>>num;
     result = ((num<=1)? 1 : log10(num)+1);
     cout<<"Number of digits "<<result<<endl;
     return 0;
 }

Bu, muhtemelen ondalık basamaktan önceki rakamları önemsediğinizi ve 10'dan küçük herhangi bir şeyin sadece 1 basamak olduğunu varsayarak, probleminizi çözmenin en basit yoludur.


1

Ira Baxter'in cevabını seviyorum. Çeşitli boyutları işleyen ve maksimum tamsayı değerleriyle ilgilenen bir şablon varyantı (üst sınır işaretini döngüden kaldırmak için güncellenir):

#include <boost/integer_traits.hpp>

template<typename T> T max_decimal()
{
    T t = 1;

    for (unsigned i = boost::integer_traits<T>::digits10; i; --i)
        t *= 10;

    return t;
}

template<typename T>
unsigned digits(T v)
{
    if (v < 0) v = -v;

    if (max_decimal<T>() <= v)
        return boost::integer_traits<T>::digits10 + 1;

    unsigned digits = 1;
    T boundary = 10;

    while (boundary <= v) {
        boundary *= 10;
        ++digits;
    }

    return digits;
}

Geliştirilmiş performansı ek testin döngüden kaldırılmasını sağlamak için platformunuzdaki her tür için sabitleri döndürmek üzere max_decimal () öğesini uzmanlaştırmanız gerekir. Yeterince sihirli bir derleyici, max_decimal () çağrısını sabit hale getirebilir, ancak bugün çoğu derleyicide uzmanlaşma daha iyidir. Haliyle, bu sürüm muhtemelen daha yavaştır çünkü max_decimal, döngüden kaldırılan testlerden daha pahalıdır.

Bütün bunları okuyucu için bir egzersiz olarak bırakacağım.


Her bir döngü yinelemesinde kontrol etmemek için önce üst sınır kontrolünü ayrı bir koşullu test yapmak istiyorsunuz.
Ira Baxter

Bu sıcaklığa 10 koymak istemezsiniz. Derleyici, t ile çarpmayı gerçek bir değişkenle çarpmayı düşünebilir ve genel amaçlı bir çarpma komutunu kullanabilir. Bunun yerine "sonuç * = 10;" derleyici kesinlikle çarpı 10 sabit ile fark ve birkaç vardiya ve ekler ile uygular, ki bu son derece hızlı.
Ira Baxter

Eğer t ile çarpma her zaman 10 ile çarpma ise, evet, derleyici güç azalması yapabilir. Bununla birlikte, bu durumda t döngü-değişmez değildir (sadece etrafta yaşadığım bir tamsayı güç fonksiyonunun bir modifikasyonudur). Doğru optimizasyon, sabit döndüren tipte uzmanlaşmadır. Bununla birlikte, bu durumda, işlevin her zaman 10'u bir güce yükseltiyor, bir güce keyfi bir tamsayı değil ve güç azaltma iyi bir kazanç sağlıyor. Bu yüzden bir değişiklik yaptım ... Bu sefer daha fazla değişiklik gerçekten bir alıştırma olarak kaldı! (Stack Overflow büyük bir zaman
batmasıdır

1
#include <stdint.h> // uint32_t [available since C99]

/// Determine the number of digits for a 32 bit integer.
/// - Uses at most 4 comparisons.
/// - (cX) 2014 adolfo.dimare@gmail.com
/// - \see http://stackoverflow.com/questions/1489830/#27669966
/**  #d == Number length vs Number of comparisons == #c
     \code
         #d | #c   #d | #c
         ---+---   ---+---
         10 | 4     5 | 4
          9 | 4     4 | 4
          8 | 3     3 | 3
          7 | 3     2 | 3
          6 | 3     1 | 3
     \endcode
*/
unsigned NumDigits32bs(uint32_t x) {
    return // Num-># Digits->[0-9] 32->bits bs->Binary Search
    ( x >= 100000u // [6-10] [1-5]
    ?   // [6-10]
        ( x >= 10000000u // [8-10] [6-7]
        ?   // [8-10]
            ( x >= 100000000u // [9-10] [8]
            ? // [9-10]
                ( x >=  1000000000u // [10] [9]
                ?   10
                :    9
                )
            : 8
            )
        :   // [6-7]
            ( x >=  1000000u // [7] [6]
            ?   7
            :   6
            )
        )
    :   // [1-5]
        ( x >= 100u // [3-5] [1-2]
        ?   // [3-5]
            ( x >= 1000u // [4-5] [3]
            ? // [4-5]
                ( x >=  10000u // [5] [4]
                ?   5
                :   4
                )
            : 3
            )
        :   // [1-2]
            ( x >=  10u // [2] [1]
            ?   2
            :   1
            )
        )
    );
}

0

Yine başka bir kod snippet'i, temel olarak Vitali'ninkiyle aynıdır, ancak ikili arama kullanır. Powers dizisi, işaretsiz tip örneği başına bir kez tembel olarak başlatılır. İmzalı tip aşırı yük eksi işareti ile ilgilenir.

#include <limits>
#include <type_traits>
#include <array>

template <class T> 
size_t NumberOfDecPositions ( T v, typename std::enable_if<std::is_unsigned<T>::value>::type* = 0 )
{
    typedef std::array<T,std::numeric_limits<T>::digits10+1> array_type;
    static array_type powers_of_10;
    if ( powers_of_10.front() == 0 )
    {
        T n = 1;
        for ( T& i: powers_of_10 )
        {
            i = n;
            n *= 10;
        }
    }

    size_t l = 0, r = powers_of_10.size(), p;
    while ( l+1 < r )
    {
        p = (l+r)/2;
        if ( powers_of_10[p] <= v )
            l = p;
        else
            r = p;
    }
    return l + 1;
};

template <class T> 
size_t NumberOfDecPositions ( T v, typename std::enable_if<std::is_signed<T>::value>::type* = 0 )
{
    typedef typename std::make_unsigned<T>::type unsigned_type;
    if ( v < 0 )
        return NumberOfDecPositions ( static_cast<unsigned_type>(-v) ) + 1;
    else
        return NumberOfDecPositions ( static_cast<unsigned_type>(v) );
}

Daha fazla optimizasyon isteyen varsa, powers dizisinin ilk öğesinin asla kullanılmadığını ve 2 kez lgöründüğünü lütfen unutmayın +1.


0

basamak sayısı VE her basamak konumunun değeri gerekliyse bunu kullanın:

int64_t = number, digitValue, digits = 0;    // or "int" for 32bit

while (number != 0) {
    digitValue = number % 10;
    digits ++;
    number /= 10;
}

digitdöngüde geçerli olarak işlenen sayı konumundaki değer verir. örneğin 1776 sayısı için basamak değeri:
6. döngüde 1. döngüde
6 , 4. döngüde
3. döngüde 7


0
// Meta-program to calculate number of digits in (unsigned) 'N'.    
template <unsigned long long N, unsigned base=10>
struct numberlength
{   // http://stackoverflow.com/questions/1489830/
    enum { value = ( 1<=N && N<base ? 1 : 1+numberlength<N/base, base>::value ) };
};

template <unsigned base>
struct numberlength<0, base>
{
    enum { value = 1 };
};

{
    assert( (1 == numberlength<0,10>::value) );
}
assert( (1 == numberlength<1,10>::value) );
assert( (1 == numberlength<5,10>::value) );
assert( (1 == numberlength<9,10>::value) );

assert( (4 == numberlength<1000,10>::value) );
assert( (4 == numberlength<5000,10>::value) );
assert( (4 == numberlength<9999,10>::value) );

Yukarıdaki "blinnov.com" dan "Pratik şaka" düzeltmesi
Adolfo

0
/// Determine the number of digits for a 64 bit integer.
/// - Uses at most 5 comparisons.
/// - (cX) 2014 adolfo.dimare@gmail.com
/// - \see http://stackoverflow.com/questions/1489830/#27670035
/**  #d == Number length vs Number of comparisons == #c
     \code
         #d | #c   #d | #c     #d | #c   #d | #c
         ---+---   ---+---     ---+---   ---+---
         20 | 5    15 | 5      10 | 5     5 | 5
         19 | 5    14 | 5       9 | 5     4 | 5
         18 | 4    13 | 4       8 | 4     3 | 4
         17 | 4    12 | 4       7 | 4     2 | 4
         16 | 4    11 | 4       6 | 4     1 | 4
     \endcode
*/
unsigned NumDigits64bs(uint64_t x) {
    return // Num-># Digits->[0-9] 64->bits bs->Binary Search
    ( x >= 10000000000ul // [11-20] [1-10]
    ?
        ( x >= 1000000000000000ul // [16-20] [11-15]
        ?   // [16-20]
            ( x >= 100000000000000000ul // [18-20] [16-17]
            ?   // [18-20]
                ( x >= 1000000000000000000ul // [19-20] [18]
                ? // [19-20]
                    ( x >=  10000000000000000000ul // [20] [19]
                    ?   20
                    :   19
                    )
                : 18
                )
            :   // [16-17]
                ( x >=  10000000000000000ul // [17] [16]
                ?   17
                :   16
                )
            )
        :   // [11-15]
            ( x >= 1000000000000ul // [13-15] [11-12]
            ?   // [13-15]
                ( x >= 10000000000000ul // [14-15] [13]
                ? // [14-15]
                    ( x >=  100000000000000ul // [15] [14]
                    ?   15
                    :   14
                    )
                : 13
                )
            :   // [11-12]
                ( x >=  100000000000ul // [12] [11]
                ?   12
                :   11
                )
            )
        )
    :   // [1-10]
        ( x >= 100000ul // [6-10] [1-5]
        ?   // [6-10]
            ( x >= 10000000ul // [8-10] [6-7]
            ?   // [8-10]
                ( x >= 100000000ul // [9-10] [8]
                ? // [9-10]
                    ( x >=  1000000000ul // [10] [9]
                    ?   10
                    :    9
                    )
                : 8
                )
            :   // [6-7]
                ( x >=  1000000ul // [7] [6]
                ?   7
                :   6
                )
            )
        :   // [1-5]
            ( x >= 100ul // [3-5] [1-2]
            ?   // [3-5]
                ( x >= 1000ul // [4-5] [3]
                ? // [4-5]
                    ( x >=  10000ul // [5] [4]
                    ?   5
                    :   4
                    )
                : 3
                )
            :   // [1-2]
                ( x >=  10ul // [2] [1]
                ?   2
                :   1
                )
            )
        )
    );
}

0

tamsayı 'X' için, herhangi bir döngü kullanmadan, rakam sayısını bilmek istediğinizde, bu çözüm sadece bir satırda bir formülde hareket eder, bu yüzden bu sorun için şimdiye kadar gördüğüm en uygun çözümdür.

 int x = 1000 ; 
 cout<<numberOfDigits = 1+floor(log10(x))<<endl ; 

INT_MAX ve ayrıca negatif sayılar için başarısız olur.
ranu

@ranu INT_MAX için nasıl başarısız olur? Bağımsız değişken ne zaman dönüştürülür double? Veya INT_MAX ondalık basamaklı bazı imkansız tamsayı girdilerinden mi bahsediyorsunuz? Hangisi de burada her cevapta başarısız olur?
Lorne Marquis

0
int numberOfDigits(int n){

    if(n<=9){
        return 1;
    }
    return 1 + numberOfDigits(n/10);
}

Taban 10 için istiyorsanız, bu yapardım, oldukça hızlı ve prolly bir yığın overflock satın sayma tamsayıları satın almayacaksınız


0
int num,dig_quant = 0;
cout<<"\n\n\t\t--Count the digits in Number--\n\n";
cout<<"Enter Number: ";
cin>>num;
for(int i = 1; i<=num; i*=10){
    if(num / i  > 0){
      dig_quant += 1;
    }
}
 cout<<"\n"<<number<<" include "<<dig_quant<<" digit"
 cout<<"\n\nGoodbye...\n\n";

0

Daha hızlı daha verimli ise, andrei alexandrescu'nun gelişimi üzerinde bir gelişme . Onun sürümü zaten naif yoldan daha hızlıydı (her basamakta 10'a bölünerek). Aşağıdaki sürüm, tüm boyutlar için en az x86-64 ve ARM'de sabit zaman ve daha hızlıdır, ancak ikili kodun iki katını kaplar, bu nedenle önbellek dostu değildir.

Bu versiyonun karşılaştırmaları, alexandrescu'nun facebook folly'deki PR'mdeki versiyonu .

Üzerinde çalışıyor unsigned, değil signed.

inline uint32_t digits10(uint64_t v) {
  return  1
        + (std::uint32_t)(v>=10)
        + (std::uint32_t)(v>=100)
        + (std::uint32_t)(v>=1000)
        + (std::uint32_t)(v>=10000)
        + (std::uint32_t)(v>=100000)
        + (std::uint32_t)(v>=1000000)
        + (std::uint32_t)(v>=10000000)
        + (std::uint32_t)(v>=100000000)
        + (std::uint32_t)(v>=1000000000)
        + (std::uint32_t)(v>=10000000000ull)
        + (std::uint32_t)(v>=100000000000ull)
        + (std::uint32_t)(v>=1000000000000ull)
        + (std::uint32_t)(v>=10000000000000ull)
        + (std::uint32_t)(v>=100000000000000ull)
        + (std::uint32_t)(v>=1000000000000000ull)
        + (std::uint32_t)(v>=10000000000000000ull)
        + (std::uint32_t)(v>=100000000000000000ull)
        + (std::uint32_t)(v>=1000000000000000000ull)
        + (std::uint32_t)(v>=10000000000000000000ull);
}

0

Ben kullanıcı doğru bir sayı kaç basamak olduğunu doğru cevap olmadığını kontrol etmek için gereken bir program üzerinde çalışıyordu, bu yüzden bir tamsayı basamak miktarını kontrol etmek için bir yol geliştirmek zorunda kaldı. Çözülmesi nispeten kolay bir şey oldu.

double check=0, exponent=1000;

while(check<=1)
{
    check=number/pow(10, exponent);
    exponent--;
}

exponent=exponent+2;
cout<<exponent<<endl;

Bu, şu anda 10 ^ 1000 basamaktan az olan sayılarla çalışan cevabım oldu (üs değeri değiştirilerek değiştirilebilir).

PS: Bu cevabın on yıl geç olduğunu biliyorum ama 2020'de buraya geldim, böylece diğer insanlar kullanabilir.


-1
template <typename type>
class number_of_decimal_digits {   
    const powers_and_max<type> mPowersAndMax;
public:
    number_of_decimal_digits(){
    }   
    inline size_t ndigits( type i) const {
        if(i<0){
             i += (i == std::numeric_limits<type>::min());
             i=-i;
        }
        const type* begin = &*mPowersAndMax.begin();
        const type* end = begin+mPowersAndMax.size();
        return 1 + std::lower_bound(begin,end,i) - begin;
    }
    inline size_t string_ndigits(const type& i) const {
        return (i<0) + ndigits(i);
    }
    inline size_t operator[](const type& i) const {
       return string_ndigits(i);
    }
};

nerede powers_and_maxElimizdeki (10^n)-1tüm nşekildedir

(10^n) < std::numeric_limits<type>::max()

ve std::numeric_limits<type>::max()bir dizide:

template <typename type>
struct powers_and_max : protected std::vector<type>{
    typedef std::vector<type> super;
    using super::const_iterator;
    using super::size;
    type& operator[](size_t i)const{return super::operator[](i)};
    const_iterator begin()const {return super::begin();} 
    const_iterator end()const {return super::end();} 
    powers_and_max() {
       const int size = (int)(log10(double(std::numeric_limits<type>::max())));
       int j = 0;
       type i = 10;
       for( ; j<size ;++j){
           push_back(i-1);//9,99,999,9999 etc;
           i*=10;
       }
       ASSERT(back()<std::numeric_limits<type>::max());
       push_back(std::numeric_limits<type>::max());
   }
};

İşte basit bir test:

number_of_decimal_digits<int>  ndd;
ASSERT(ndd[0]==1);
ASSERT(ndd[9]==1);
ASSERT(ndd[10]==2);
ASSERT(ndd[-10]==3);
ASSERT(ndd[-1]==2);
ASSERT(ndd[-9]==2);
ASSERT(ndd[1000000000]==10);
ASSERT(ndd[0x7fffffff]==10);
ASSERT(ndd[-1000000000]==11);
ASSERT(ndd[0x80000000]==11);

Elbette sıralı bir kümenin başka herhangi bir uygulaması için kullanılabilir powers_and_maxve kümelenme olacağına dair bilgi varsa, ancak kümenin nerede olabileceğine dair bilgi yoksa belki de kendini ayarlayan bir ağaç uygulaması en iyi olabilir


-1

etkili yol

int num;
int count = 0;
while(num)
{
   num /= 10;
   ++count;
}

#include <iostream>

int main()
{
   int num;
   std::cin >> num;

   std::cout << "number of digits for " << num << ": ";

   int count = 0;
   while(num)
   {
      num /= 10;
      ++count;
   }

   std::cout << count << '\n';

   return 0;
}

-1

Tercih edilen çözümün C ++ 11 güncellemesi:

#include <limits>
#include <type_traits>
        template <typename T>
        typename std::enable_if<std::numeric_limits<T>::is_integer, unsigned int>::type
        numberDigits(T value) {
            unsigned int digits = 0;
            if (value < 0) digits = 1;
            while (value) {
                value /= 10;
                ++digits;
            }
            return digits;
        }

double ile şablon başlatmayı önler, et. ark.


-1
int numberOfDigits(double number){
    if(number < 0){
        number*=-1;
    }
    int i=0;
        while(number > pow(10, i))
            i++;    
    cout << "This number has " << i << " digits" << endl;
    return i;
}

-2

Bunu yapmanın yolu:

   int digitcount(int n)
    {
        int count = 1;
        int temp = n;
        while (true)
        {
            temp /= 10;
            if (temp != 0) ++count;
            if (temp == 0) break;
        }

        return count;
    }

2
while true / break sendromu: D
Петър Петров

-1 Bu, ilk cevabın altı yıl önce verdiği aynı yaklaşımdır ve hiçbir şey katmaz (aslında önemli ölçüde daha kötüdür).

-4

İşte farklı bir yaklaşım:

digits = sprintf(numArr, "%d", num);    // where numArr is a char array
if (num < 0)
    digits--;

Bu etkili olmayabilir, sadece başkalarının önerdiklerinden farklı bir şey olabilir.


4
İstek son derece etkili oldu. Bu tam tersidir.
Ira Baxter
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.