Jaki jest bardzo skuteczny sposób określania liczby cyfr w liczbie całkowitej w C ++?
Jaki jest bardzo skuteczny sposób określania liczby cyfr w liczbie całkowitej w C ++?
Odpowiedzi:
Cóż, najbardziej wydajnym sposobem, zakładając, że znasz rozmiar liczby całkowitej, byłoby wyszukiwanie. Powinien być szybszy niż podejście oparte na znacznie krótszym logarytmie. Jeśli nie zależy ci na liczeniu „-”, usuń + 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];
}
Najprościej jest zrobić:
unsigned GetNumberOfDigits (unsigned i)
{
return i > 0 ? (int) log10 ((double) i) + 1 : 1;
}
log10 jest zdefiniowany w <cmath>
lub <math.h>
. Musisz to profilować, aby sprawdzić, czy jest szybszy niż którykolwiek z innych opublikowanych tutaj. Nie jestem pewien, jak solidne jest to pod względem precyzji punktu zmiennoprzecinkowego. Ponadto argument jest bez znaku jako wartości ujemne, a log tak naprawdę się nie miesza.
-fpfast
możesz zobaczyć użycie narzędzi SSE zamiast x87, co daje mniej gwarancji na precyzję IIRC. ale domyślnie nie ma problemu.
Być może źle zrozumiałem pytanie, ale czy to nie działa?
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++; }
Uwaga: „0” będzie miało 0 cyfr! Jeśli potrzebujesz, aby 0 wyglądało na 1 cyfrę, użyj:
int digits = 0; do { number /= 10; digits++; } while (number != 0);
(Dzięki Kevin Fegan)
Na koniec użyj programu profilującego, aby wiedzieć, która ze wszystkich odpowiedzi tutaj będzie szybsza na twoim komputerze ...
Żart: Jest najbardziej efektywny sposób (liczba cyfr jest obliczana w czasie kompilacji):
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 };
};
Może być przydatne do określenia szerokości wymaganej dla pola liczbowego w formatowaniu, elementach wejściowych itp.
0
niepowodzeniem w przypadku bazy 1
:) i daje dzielenie przez zero, jeśli podstawa jest podana jako 0
. Można to jednak naprawić. W każdym razie czepiam się bardzo starego postu, więc przepraszam, po prostu myślę, że to nie musi być żartem i może być przydatne.
Zobacz Hacks Bit Twiddling, aby zapoznać się z dużo krótszą wersją zaakceptowanej odpowiedzi. Ma to również tę zaletę, że można szybciej znaleźć odpowiedź, jeśli dane wejściowe mają rozkład normalny, sprawdzając najpierw duże stałe. (v >= 1000000000)
łapie 76% wartości, więc sprawdzenie, czy pierwsze będzie średnio szybsze.
int x = 1000;
int numberOfDigits = x ? static_cast<int>(log10(abs(x))) + 1 : 1;
Poprzedni plakat sugerował pętlę dzielącą przez 10. Ponieważ mnożenia na nowoczesnych komputerach są dużo szybsze, poleciłbym zamiast tego następujący kod:
int digits = 1, pten=10; while ( pten <= number ) { digits++; pten*=10; }
Architektura ppc ma instrukcję zliczania bitów. Dzięki temu możesz określić podstawę logu 2 dodatniej liczby całkowitej w jednej instrukcji. Na przykład wersja 32-bitowa to:
#define log_2_32_ppc(x) (31-__cntlzw(x))
Jeśli możesz obsłużyć mały margines błędu przy dużych wartościach, możesz przekonwertować to na dziennik o podstawie 10 za pomocą kilku kolejnych instrukcji:
#define log_10_estimate_32_ppc(x) (9-(((__cntlzw(x)*1233)+1545)>>12))
Jest to specyficzne dla platformy i nieco niedokładne, ale również nie wymaga rozgałęzień, dzielenia ani konwersji na zmiennoprzecinkowe. Wszystko zależy od tego, czego potrzebujesz.
Znam tylko instrukcje ppc, ale inne architektury powinny mieć podobne instrukcje.
#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;
}
Jest to prawdopodobnie najprostszy sposób rozwiązania problemu, zakładając, że zależy Ci tylko na cyfrach przed przecinkiem i przyjmując, że cokolwiek mniej niż 10 to tylko 1 cyfra.
Podoba mi się odpowiedź Iry Baxtera. Oto wariant szablonu, który obsługuje różne rozmiary i radzi sobie z maksymalnymi wartościami całkowitymi (zaktualizowanymi w celu podniesienia górnej granicy wyewidencjonowania z pętli):
#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;
}
Aby faktycznie uzyskać lepszą wydajność z wyciągania dodatkowego testu z pętli, musisz wyspecjalizować max_decimal (), aby zwracać stałe dla każdego typu na twojej platformie. Wystarczająco magiczny kompilator mógłby zoptymalizować wywołanie max_decimal () do stałej, ale obecnie większość kompilatorów specjalizuje się lepiej. W obecnym kształcie ta wersja jest prawdopodobnie wolniejsza, ponieważ max_decimal kosztuje więcej niż testy usunięte z pętli.
Zostawię to wszystko jako ćwiczenie dla czytelnika.
#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
)
)
);
}
Kolejny fragment kodu, działający w zasadzie tak samo jak Vitali, ale wykorzystujący wyszukiwanie binarne. Tablica Powers jest inicjowana z opóźnieniem raz na wystąpienie typu bez znaku. Przeciążenie typu ze znakiem obsługuje znak minus.
#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) );
}
Jeśli komuś zależy na dalszej optymalizacji, pamiętaj, że pierwszy element tablicy mocy nigdy nie jest używany, a l
pojawia się +1
2 razy.
w przypadku, gdy potrzebna jest liczba cyfr ORAZ wartość każdej pozycji cyfry użyj tego:
int64_t = number, digitValue, digits = 0; // or "int" for 32bit
while (number != 0) {
digitValue = number % 10;
digits ++;
number /= 10;
}
digit
podaje wartość w pozycji numeru, która jest aktualnie przetwarzana w pętli. na przykład dla numeru 1776 wartość cyfry to:
6 w 1. pętli
7 w 2. pętli
7 w 3. pętli
1 w czwartej pętli
// 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
)
)
)
);
}
dla liczby całkowitej `` X '' chcesz znać liczbę cyfr, w porządku bez użycia żadnej pętli, to rozwiązanie działa w jednej formule tylko w jednym wierszu, więc jest to najbardziej optymalne rozwiązanie tego problemu, jakie kiedykolwiek widziałem.
int x = 1000 ;
cout<<numberOfDigits = 1+floor(log10(x))<<endl ;
double
? A może masz na myśli jakieś niemożliwe dane wejściowe w postaci liczb całkowitych z cyframi dziesiętnymi INT_MAX? Która z poniższych odpowiedzi również zawiodłaby?
int numberOfDigits(int n){
if(n<=9){
return 1;
}
return 1 + numberOfDigits(n/10);
}
Oto, co bym zrobił, jeśli chcesz to dla podstawy 10, jest dość szybki i prawdopodobnie nie dostaniesz stosu overflock kup licząc liczby całkowite
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";
Jeśli szybsze jest bardziej wydajne, oznacza to poprawę w stosunku do poprawy Andrei Alexandrescu . Jego wersja była już szybsza niż naiwny sposób (dzielenie przez 10 przy każdej cyfrze). Poniższa wersja ma stały czas i jest szybsza przynajmniej na x86-64 i ARM dla wszystkich rozmiarów, ale zajmuje dwa razy więcej kodu binarnego, więc nie jest tak przyjazna dla pamięci podręcznej.
Benchmarki dla tej wersji w porównaniu z wersją alexandrescu na moim PR na Facebooku szaleństwo .
Działa dalej unsigned
, nie 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);
}
Pracowałem nad programem, który wymagał ode mnie sprawdzenia, czy użytkownik poprawnie odpowiedział, ile cyfr znajduje się w liczbie, więc musiałem opracować sposób sprawdzania liczby cyfr w liczbie całkowitej. Okazało się, że było to stosunkowo łatwe do rozwiązania.
double check=0, exponent=1000;
while(check<=1)
{
check=number/pow(10, exponent);
exponent--;
}
exponent=exponent+2;
cout<<exponent<<endl;
To była moja odpowiedź, która obecnie działa z liczbami mniejszymi niż 10 ^ 1000 cyfr (można to zmienić, zmieniając wartość wykładnika).
PS Wiem, że ta odpowiedź jest opóźniona o dziesięć lat, ale dotarłem tu w 2020 roku, więc inni mogą z niej skorzystać.
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);
}
};
gdzie powers_and_max
mamy (10^n)-1
dla wszystkich n
takie, że
(10^n) <
std::numeric_limits<type>::max()
i std::numeric_limits<type>::max()
w tablicy:
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());
}
};
oto prosty 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);
Oczywiście każda inna implementacja uporządkowanego zbioru może być użyta do powers_and_max
i jeśli byłaby wiedza, że będzie klaster, ale brak wiedzy o tym, gdzie może być klaster, być może samodopasowująca się implementacja drzewa może być najlepsza
efektywny sposób
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;
}
Aktualizacja preferowanego rozwiązania w C ++ 11:
#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;
}
zapobiega tworzeniu się szablonów za pomocą double, et. glin.
Oto mój sposób na zrobienie tego:
int digitcount(int n)
{
int count = 1;
int temp = n;
while (true)
{
temp /= 10;
if (temp != 0) ++count;
if (temp == 0) break;
}
return count;
}
Oto inne podejście:
digits = sprintf(numArr, "%d", num); // where numArr is a char array
if (num < 0)
digits--;
To może nie być wydajne, po prostu coś innego niż to, co sugerowali inni.