C ++ 'da bir tamsayıda kaç basamak bulunduğunu belirlemenin çok etkili bir yolu nedir ?
C ++ 'da bir tamsayıda kaç basamak bulunduğunu belirlemenin çok etkili bir yolu nedir ?
Yanıtlar:
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];
}
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.
-fpfast
x87 yerine SSE instrinsics kullanımını görebiliyorsanız, bu durum IIRC hassasiyetinde daha az garanti sağlar. ama varsayılan olarak sorun yok.
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)))))))));
}
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 ...
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.
0
ve 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.
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.
int x = 1000;
int numberOfDigits = x ? static_cast<int>(log10(abs(x))) + 1 : 1;
Ö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; }
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ı.
#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.
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.
#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
)
)
);
}
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 l
göründüğünü lütfen unutmayın +1
.
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;
}
digit
dö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
// 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) );
/// 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
)
)
)
);
}
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 ;
double
? Veya INT_MAX ondalık basamaklı bazı imkansız tamsayı girdilerinden mi bahsediyorsunuz? Hangisi de burada her cevapta başarısız olur?
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
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";
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);
}
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.
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_max
Elimizdeki (10^n)-1
tü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_max
ve 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
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;
}
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.
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;
}
İş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.