Qual é uma maneira muito eficiente de determinar quantos dígitos existem em um número inteiro em C ++?
Qual é uma maneira muito eficiente de determinar quantos dígitos existem em um número inteiro em C ++?
Respostas:
Bem, a maneira mais eficiente, presumindo que você saiba o tamanho do número inteiro, seria uma pesquisa. Deve ser mais rápido que a abordagem baseada em logaritmo, muito mais curta. Se você não se importa em contar o '-', remova o + 1.
// 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];
}
A maneira mais simples é fazer:
unsigned GetNumberOfDigits (unsigned i)
{
return i > 0 ? (int) log10 ((double) i) + 1 : 1;
}
log10 é definido em <cmath>
ou <math.h>
. Você precisaria criar um perfil para ver se é mais rápido do que qualquer um dos outros postados aqui. Não tenho certeza de quão robusto isso é com relação à precisão do ponto de flutuação. Além disso, o argumento não é assinado como valores negativos e o log realmente não se mistura.
-fpfast
você usar, poderá ver o uso de instrumentos SSE em vez de x87, o que gera menos garantia no IIRC de precisão. mas por padrão não há problema.
Talvez eu tenha entendido mal a pergunta, mas isso não acontece?
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++; }
Nota: "0" terá 0 dígitos! Se você precisar que 0 pareça ter 1 dígito, use:
int digits = 0; do { number /= 10; digits++; } while (number != 0);
(Obrigado Kevin Fegan)
No final, use um criador de perfil para saber quais das respostas aqui serão mais rápidas na sua máquina ...
Piada prática: Esta é a maneira mais eficiente (o número de dígitos é calculado em tempo de compilação):
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 };
};
Pode ser útil para determinar a largura necessária para o campo numérico na formatação, elementos de entrada etc.
0
e também falha na base 1
:) e gera erros de divisão por zero se a base for dada como 0
. Pode ser corrigido embora. De qualquer forma, estou procurando um post muito antigo, desculpe, é só que acho que isso não precisa ser uma piada e pode realmente ser útil.
Veja Bit Twiddling Hacks para uma versão muito mais curta da resposta que você aceitou. Também tem o benefício de encontrar a resposta mais cedo se sua entrada for normalmente distribuída, verificando primeiro as grandes constantes. (v >= 1000000000)
captura 76% dos valores, portanto, verificar se o primeiro será, em média, mais rápido.
int x = 1000;
int numberOfDigits = x ? static_cast<int>(log10(abs(x))) + 1 : 1;
Um pôster anterior sugeria um loop dividido por 10. Como as multiplicações nas máquinas modernas são muito mais rápidas, recomendo o seguinte código:
int digits = 1, pten=10; while ( pten <= number ) { digits++; pten*=10; }
A arquitetura ppc possui algumas instruções de contagem. Com isso, você pode determinar a base de log 2 de um número inteiro positivo em uma única instrução. Por exemplo, 32 bits seria:
#define log_2_32_ppc(x) (31-__cntlzw(x))
Se você conseguir lidar com uma pequena margem de erro em valores grandes, poderá convertê-lo na base 10 do log com mais algumas instruções:
#define log_10_estimate_32_ppc(x) (9-(((__cntlzw(x)*1233)+1545)>>12))
Isso é específico da plataforma e um pouco impreciso, mas também não envolve ramificações, divisão ou conversão em ponto flutuante. Tudo depende do que você precisa.
Conheço apenas as instruções ppc, mas outras arquiteturas devem ter instruções semelhantes.
#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;
}
Essa é provavelmente a maneira mais simples de resolver seu problema, supondo que você se preocupe apenas com dígitos antes do decimal e supondo que menos de 10 seja apenas 1 dígito.
Eu gosto da resposta de Ira Baxter. Aqui está uma variante de modelo que lida com os vários tamanhos e lida com os valores máximos inteiros (atualizados para elevar a verificação do limite superior do loop):
#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;
}
Para realmente obter o desempenho aprimorado ao elevar o teste adicional para fora do loop, você precisa especializar max_decimal () para retornar constantes para cada tipo em sua plataforma. Um compilador suficientemente mágico pode otimizar a chamada para max_decimal () para uma constante, mas a especialização é melhor com a maioria dos compiladores hoje. Tal como está, esta versão é provavelmente mais lenta porque max_decimal custa mais do que os testes removidos do loop.
Vou deixar tudo isso como um exercício para o leitor.
#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
)
)
);
}
Outro trecho de código, fazendo basicamente o mesmo que o de Vitali, mas emprega pesquisa binária. A matriz Powers é inicializada com preguiça uma vez por instância do tipo não assinado. A sobrecarga do tipo assinado cuida do sinal de menos.
#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) );
}
Se alguém se preocupa com uma otimização adicional, observe que o primeiro elemento do conjunto de potências nunca é usado e l
aparece +1
duas vezes.
caso seja necessário o número de dígitos E o valor de cada posição de dígito, use:
int64_t = number, digitValue, digits = 0; // or "int" for 32bit
while (number != 0) {
digitValue = number % 10;
digits ++;
number /= 10;
}
digit
fornece o valor na publicação numérica atualmente processada no loop. por exemplo, para o número 1776, o valor do dígito é:
6 no 1º loop
7 no 2º loop
7 no 3º loop
1 no 4º loop
// 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
)
)
)
);
}
para o número inteiro 'X', você quer saber o número de dígitos, tudo bem, sem usar nenhum loop, esta solução atua apenas em uma fórmula em uma linha, portanto, esta é a solução mais ideal que já vi para esse problema.
int x = 1000 ;
cout<<numberOfDigits = 1+floor(log10(x))<<endl ;
double
? Ou você está se referindo a alguma entrada inteira impossível com dígitos decimais INT_MAX? O que também falharia em todas as outras respostas aqui?
int numberOfDigits(int n){
if(n<=9){
return 1;
}
return 1 + numberOfDigits(n/10);
}
Isso é o que eu faria, se você o quisesse para a base 10. É muito rápido e você provavelmente não obterá um overflock de pilha, contando números inteiros
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";
Se mais rápido for mais eficiente, isso é uma melhoria na melhoria de andrei alexandrescu . Sua versão já era mais rápida que a ingênua (dividindo por 10 a cada dígito). A versão abaixo é de tempo constante e mais rápida, pelo menos, em x86-64 e ARM para todos os tamanhos, mas ocupa o dobro do código binário, por isso não é tão compatível com o cache.
Benchmarks para esta versão vs a versão de alexandrescu no meu PR no facebook folly .
Funciona unsigned
, não 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);
}
Eu estava trabalhando em um programa que exigia que eu verifique se o usuário respondeu corretamente quantos dígitos há em um número, então tive que desenvolver uma maneira de verificar a quantidade de dígitos em um número inteiro. Acabou sendo uma coisa relativamente fácil de resolver.
double check=0, exponent=1000;
while(check<=1)
{
check=number/pow(10, exponent);
exponent--;
}
exponent=exponent+2;
cout<<exponent<<endl;
Isso acabou sendo minha resposta, que atualmente trabalha com números com menos de 10 ^ 1000 dígitos (pode ser alterada alterando o valor do expoente).
PS: Eu sei que essa resposta está dez anos atrasada, mas cheguei aqui em 2020 para que outras pessoas possam usá-la.
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);
}
};
onde powers_and_max
temos (10^n)-1
para todos n
que
(10^n) <
std::numeric_limits<type>::max()
e std::numeric_limits<type>::max()
em uma matriz:
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());
}
};
aqui está um teste simples:
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);
É claro que qualquer outra implementação de um conjunto ordenado pode ser usada powers_and_max
e se havia conhecimento de que haveria armazenamento em cluster, mas nenhum conhecimento de onde o cluster poderia estar talvez uma implementação de árvore autoajustável poderia ser melhor
forma efetiva
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;
}
Atualização em C ++ 11 da solução preferida:
#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;
}
previne instanciação de template com double, et. al.
Esta é a minha maneira de fazer isso:
int digitcount(int n)
{
int count = 1;
int temp = n;
while (true)
{
temp /= 10;
if (temp != 0) ++count;
if (temp == 0) break;
}
return count;
}
Aqui está uma abordagem diferente:
digits = sprintf(numArr, "%d", num); // where numArr is a char array
if (num < 0)
digits--;
Isso pode não ser eficiente, apenas algo diferente do que os outros sugeriram.