Эффективный способ определить количество цифр в целом числе


145

Что является очень эффективным способом определения количества цифр в целом числе в C ++?


11
В какой базе? 2? 10?
Джейкоб Кралл

2
Я хотел бы сделать это в базе 10
Сет

1
Однажды я задал связанный вопрос: как вы можете получить первую цифру в int? Многие из тех же методологий, что и ниже, были использованы в ответах людей. Вот ссылка на случай, если она имеет отношение к вашей задаче [ stackoverflow.com/questions/701322/]
Dinah

Подходит ли встроенная сборка?
György Andrasek

1
Хотя все эти ответы относятся к основанию 10, его довольно легко изменить, чтобы вычислить результат для любой желаемой базы.
Ира Бакстер

Ответы:


106

Ну, самый эффективный способ, предполагая, что вы знаете размер целого числа, это поиск. Должен быть быстрее, чем подход, основанный на гораздо более коротком логарифме. Если вам не нужно считать «-», удалите + 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];
}

5
Наверное, быстрее, чем мой ответ, молодец. Для повышения эффективности, если вы знаете, что ваши входные числа будут в основном небольшими (я предполагаю, что их меньше 100 000), то поменяйте местами тесты: if (x <10) return 1; if (x <100) возвращает 2; и т.д., чтобы функция выполняла меньше тестов и быстрее выходила.
squelart

29
Или, возможно, измените порядок и вложите операторы if, чтобы выполнить бинарный поиск вместо линейного поиска.
dave4420,

1
Это не очень хорошая идея. Что происходит, когда архитектура расширяется до 256-битных целых чисел. Вы должны помнить, чтобы вернуться и изменить этот код. В реальной жизни этого не произойдет, и, скорее всего, это будет использоваться для создания буфера правильного размера, который вы сейчас открываете себе для всех видов проблем с переполнением буфера на больших архитектурах.
Мартин Йорк,

3
при условии равномерного распределения чисел обратный линейный поиск (начиная с макс. цифр до 1) может быть в среднем быстрее, чем бинарный поиск, поскольку чисел с N цифрами значительно больше, чем с N-1 цифрами graphics.stanford.edu/~ Сеандер /…
фа.

6
Я бы не стал сильно беспокоиться о 256 или 128 битных целых числах. Если вам не нужно посчитать количество электронов во Вселенной (10 ^ 78 в прошлый раз, когда я это делал), 64 бита будут работать довольно хорошо. 32-битные машины просуществовали ~ ~ 15 лет. Я предполагаю, что 64-битные машины будут работать намного дольше. Для больших чисел подойдет многоточная арифметика, и я сомневаюсь, что эффективность подсчета цифр будет иметь значение.
Ира Бакстер

74

Самый простой способ это сделать:

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

log10 определяется в <cmath>или <math.h>. Вам нужно будет профилировать это, чтобы увидеть, если это быстрее, чем любой из других, размещенных здесь. Я не уверен, насколько это надежно в отношении точности с плавающей точкой. Кроме того, аргумент не подписан как отрицательные значения, и лог не смешивается.


7
Для 32-битных и 56-битных операций с плавающей запятой это, вероятно, работает. Если входное значение длинное (64 бита), 56-битовая логарифм с двойной точностью может привести к неправильному ответу в случае значений вблизи больших значений 10 ^ n. Ожидайте неприятности выше 2 ^ 50.
Ира Бакстер

1
Также возникает вопрос, насколько точны функции журнала. Я не проверял, насколько они точны в современных библиотеках, и мне было бы неудобно слепо полагать, что они хороши для одной части из миллиарда.
Дэвид Торнли

@DavidThornley: журнал или другие математические функции являются совершенно точными, если они не указаны в командной строке компилятора. некоторые из них будут преобразованы в встроенные функции x86 во время компиляции. некоторые не существуют и будут расширяться в формулы существующих встроенных функций. Например, если -fpfastвы используете, вы можете увидеть использование SSE instrinsics вместо x87, что дает меньшую гарантию точности IIRC. но по умолчанию нет проблем.
v.oddou

@DavidThornley: это больше, чем точность. Вопрос в том, гарантировано ли или нет, что log10 (10 ^ k) ≥ k для всех соответствующих k. То есть гарантируется, что любая неизбежная ошибка округления идет в правильном направлении. k + eps в результате работает, k - eps нет. И «Совершенно точно» наивно.
gnasher729

1
Тест i> 0 может быть оптимизирован для i> 9
Пэт

60

Возможно, я неправильно понял вопрос, но разве это не так?

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
И я не удивлюсь, если это решение будет самым быстрым.
VisioN

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

Примечание: «0» будет иметь 0 цифр! Если вам нужно, чтобы 0 отображало 1 цифру, используйте:

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

(Спасибо Кевину Фегану)

В конце концов, используйте профилировщик, чтобы узнать, какой из всех ответов здесь будет быстрее на вашем компьютере ...


3
Это может или не может быть быстрее, чем подход с развернутым циклом, который я выбрал - вам нужно профилировать разницу (должно быть незначительным в долгосрочной перспективе).
Виталий

Согласитесь, профилирование - это единственный способ узнать наверняка! Я обновил свой ответ этим комментарием, поскольку ответ ce S (log10 ()) Бена С. исчез.
squelart

11

Розыгрыши: Это самый эффективный способ (количество цифр вычисляются во время компиляции):

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 };
};

Может быть полезно определить ширину, необходимую для числового поля при форматировании, элементах ввода и т. Д.


4
Во-первых, ваше решение не работает для 0. Во-вторых, ваше решение неприменимо к общему случаю переменной. В-третьих, если вы используете постоянный литерал, вы уже знаете, сколько у него цифр.
Виталий

Это работает для 0 тоже. Это также работает для любой базы. Остальные действительные моменты, которые я уже изложил.
blinnov.com

3
Я не думаю, что это на самом деле. Он не работает, 0а также не работает на базе 1:) и дает деление на ноль ошибок, если база задана как 0. Это может быть исправлено, хотя. В любом случае, я придираюсь к очень старому посту, так что извините, просто я думаю, что это не должно быть шуткой и может быть действительно полезным.
TJM

9

См. Bit Twiddling Hacks для более короткой версии ответа, который вы приняли. Преимущество также заключается в том, что вы можете быстрее найти ответ, если ваш входной сигнал обычно распределяется, сначала проверяя большие константы. (v >= 1000000000)ловит 76% значений, поэтому проверка, что первый будет в среднем быстрее.


Неясно, происходит ли на самом деле быстрое переключение битов. Даже в наихудшем случае мой модифицированный подход требует 4 сравнений (возможно, я смогу уменьшить его до 3, если я рассмотрю разделение дальше, хотя это выглядит маловероятным). Я серьезно сомневаюсь, что это побьет арифметические операции + загрузки памяти (хотя при достаточном доступе они исчезают в кэше процессора). Помните, что в приведенном ими примере они также скрывают базу журналов 2 как некую абстрактную функцию IntegerLogBase2 (которая сама по себе недешева).
Виталий

Так же, как продолжение, да, если числа нормально распределены, проверка порядка выполняется быстрее. Тем не менее, он имеет вырожденный случай быть в два раза медленнее в худшем случае. Разделенный подход по количеству цифр вместо пространства ввода означает, что поведение не имеет вырожденного случая и всегда работает оптимально. Кроме того, помните, что вы предполагаете, что числа будут распределены равномерно. На самом деле, они более склонны следовать некоторым распределением , связанные с <a href=" en.wikipedia.org/wiki/...> будет моя догадка.
Виталий

Взломанные бит-хедлеры не быстрее, чем описанный выше метод разбиения, но они потенциально интересны, если у вас есть более общий случай, такой как float.
Корвин Джой

1
Немного взлома предлагает способ получить int log10, учитывая int log2. Он предлагает несколько способов получить int log2, в основном с использованием нескольких сравнений / ветвей. (Я думаю, что вы недооцениваете стоимость непредсказуемых веток, Виталий). Если вы можете использовать встроенный x86 asm, инструкция BSR даст вам int log2 значения (т. Е. Битовый индекс старшего значащего установленного бита). Это немного медленно на K8 (задержка 10 циклов), но быстро на Core 2 (задержка 2 или 3 цикла). Даже на K8 вполне может быть быстрее, чем сравнения.
Питер Кордес

На K10 lzcnt считает начальные нули, поэтому он почти такой же, как bsr, но ввод 0 больше не является особым случаем с неопределенными результатами. Задержки: BSR: 4, LZCNT: 2.
Питер Кордес

8

преобразовать в строку, а затем использовать встроенные функции

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
Хотя это эффективно с точки зрения LOC, как отмечено в принятом ответе, использование журнала, вероятно, не даст наилучшей производительности.
Ян

@Ian Почему бы и нет? Это всего лишь пара инструкций FPU. Мили лучше всех веток и петель в других ответах.
маркиз Лорн

5

Предыдущий автор предлагал цикл, который делится на 10. Поскольку умножение на современных машинах происходит намного быстрее, я бы порекомендовал следующий код:

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

1
дьявол кроется в деталях - что происходит с скажем std :: numeric_limits <int> :: max == number - может возникнуть проблема с завершением
pgast

2
Если вас беспокоит этот случай, вы можете добавить еще один IF для обработки очень больших значений.
Ира Бакстер

2
Я должен заметить, что на машинах с архитектурой x86 умножение на константу 10, используемое в этом случае, может фактически быть реализовано компилятором как LEA R2, [8 * R1 + R1], ADD R1, R2, так что требуется максимум 2 такта. Умножение на переменные занимает десятки часов, а деление намного хуже.
Ира Бакстер

Преимущество метода деления состоит в том, что вам не нужно беспокоиться об отрицательных числах.
Йоханнес Шауб -

1
Я сравнил подход умножения (с потрясающими, чтобы убрать проблему знака) с подходом деления. На моей машине подход деления на 2 раза медленнее, чем подход умножения. Является ли это преждевременной оптимизацией или нет, зависит от того, где и как это называется.
Spacemoose

5

В архитектуре PPC есть команда подсчета битов. При этом вы можете определить лог-базу 2 положительного целого числа в одной инструкции. Например, 32 бит будет:

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

Если вы можете обработать небольшую погрешность при больших значениях, вы можете преобразовать ее в log 10 с помощью нескольких инструкций:

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

Это зависит от платформы и немного неточно, но также не требует ветвлений, деления или преобразования в плавающую точку. Все зависит от того, что вам нужно.

Я знаю только инструкции ppc, но другие архитектуры должны иметь аналогичные инструкции.


Это решение вычисляет log2 (15) = 4 бита и log2 (9) = 4 бита. Но 15 и 9 для печати требуется различное количество десятичных цифр. Так что это не сработает, если только вы не возражаете против того, чтобы ваши цифры иногда печатались слишком много цифр. Но в этом случае вы всегда можете выбрать «10» в качестве ответа для int.
Ира Бакстер

Вау, приблизительная функция. Ницца.
doug65536

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;
 }

Вероятно, это самый простой способ решения вашей проблемы, предполагая, что вы заботитесь только о цифрах перед десятичной дробью, и предполагая, что значение меньше 10 - это всего лишь 1 цифра.


1

Мне нравится ответ Айры Бакстер. Вот вариант шаблона, который обрабатывает различные размеры и имеет дело с максимальными целочисленными значениями (обновлен, чтобы вывести верхнюю границу из цикла):

#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;
}

Чтобы на самом деле получить улучшенную производительность от включения дополнительного теста из цикла, вам нужно специализировать max_decimal (), чтобы возвращать константы для каждого типа на вашей платформе. Достаточно волшебный компилятор может оптимизировать вызов max_decimal () для константы, но специализация лучше для большинства компиляторов сегодня. В настоящее время эта версия, вероятно, медленнее, потому что max_decimal стоит больше, чем тесты, удаленные из цикла.

Я оставлю все это в качестве упражнения для читателя.


Вы хотите, чтобы верхний предел проверял сначала отдельную условную проверку, чтобы не проверять ее на каждой итерации цикла.
Ира Бакстер

Вы не хотите ставить 10 в этот темп. Компилятор может рассмотреть умножение на t как умножение на вещественную переменную и использовать универсальную инструкцию умножения. Если вы вместо этого написали "result * = 10;" компилятор обязательно заметит умножение на константу 10 и выполнит это с несколькими сдвигами и сложениями, что очень быстро.
Ира Бакстер

Если умножение на t всегда было умножением на 10, тогда да, компилятор мог бы сделать уменьшение прочности. Однако в этом случае t не является инвариантным к петле (это всего лишь модификация целочисленной степенной функции, которая у меня лежала). Правильная оптимизация - это специализация на типе, возвращающем константу. Тем не менее, вы правы в том, что в этом случае функция всегда увеличивает число до 10, а не произвольное целое число, а уменьшение силы дает хороший выигрыш. Итак, я внес изменения ... На этот раз дальнейшие изменения действительно остались в качестве упражнения! (Переполнение стека - это большое
времяпровождение

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

Еще один фрагмент кода, который делает то же самое, что и Виталий, но использует бинарный поиск. Массив Powers инициализируется лениво один раз для экземпляра без знака. Перегрузка со знаком типа заботится о знаке минус.

#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) );
}

Если кого-то волнует дальнейшая оптимизация, обратите внимание, что первый элемент массива полномочий никогда не используется, а lпоявляется +12 раза.


0

в случае, если необходимо количество цифр И значение каждой позиции цифры, используйте это:

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

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

digitдает значение в числовой позиции, которая в настоящее время обрабатывается в цикле. например, для числа 1776 значение цифры:
6 в 1-м цикле
7 во 2-м цикле
7 в 3-м цикле
1 в 4-м цикле


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) );

Исправление для «Практической шутки» из «blinnov.com» выше
Адольфо

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

для целого числа 'X' вы хотите знать количество цифр, хорошо, без использования какого-либо цикла, это решение действует только в одной формуле в одной строке, так что это самое оптимальное решение, которое я когда-либо видел для этой проблемы.

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

Сбой для INT_MAX, а также для отрицательных чисел.
Ран

@ranu Сбой для INT_MAX как? Когда аргумент преобразуется в double? Или вы имеете в виду какой-то невозможный целочисленный ввод с десятичными цифрами INT_MAX? Который также потерпит неудачу при любом другом ответе здесь?
маркиз Лорн

0
int numberOfDigits(int n){

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

Это то, что я бы сделал, если вы хотите это для базы 10. Это довольно быстро, и вы, скорее всего, не получите переполнение стека, подсчитывающие целые числа


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

Если быстрее, тем эффективнее, это улучшение Андрея Александреску . Его версия была уже быстрее, чем наивный путь (деление на 10 на каждую цифру). Приведенная ниже версия имеет постоянное время и быстрее, по крайней мере, для x86-64 и ARM для всех размеров, но занимает в два раза больше двоичного кода, поэтому она не так удобна для кэша.

Тесты для этой версии против версии Александра на моей рекламе на фоллике Facebook .

Работает на unsignedнет 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

Я работал над программой, которая требовала, чтобы я проверял, правильно ли пользователь ответил, сколько цифр в числе, поэтому мне пришлось разработать способ проверки количества цифр в целом числе. Это оказалось относительно легко решить.

double check=0, exponent=1000;

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

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

В конечном итоге это был мой ответ, который в настоящее время работает с числами с менее чем 10 ^ 1000 цифр (можно изменить, изменив значение показателя степени).

PS Я знаю, что этот ответ опоздал на десять лет, но я пришел сюда в 2020 году, чтобы другие могли его использовать.


-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);
    }
};

где powers_and_maxу нас (10^n)-1для всех nтаких, что

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

и std::numeric_limits<type>::max()в массиве:

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());
   }
};

вот простой тест:

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);

Конечно, можно использовать любую другую реализацию упорядоченного набора, powers_and_maxи если бы было известно, что кластеризация будет существовать, но не было бы знания о том, где может быть кластер, возможно, лучше всего подойдет саморегулирующаяся реализация дерева.


-1

эффективный способ

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

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;
        }

предотвращает создание шаблона с помощью double, et. и др.


-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

Это мой способ сделать это:

   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
в то время как синдром истины / разрыва: D
Петър Петров

-1 это тот же подход, который первый ответ дал шесть лет назад, и он ничего не добавляет (на самом деле он значительно хуже).

-4

Вот другой подход:

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

Это может быть неэффективно, просто что-то отличное от того, что предлагали другие.


4
Запрос был чрезвычайно эффективным. Это наоборот.
Ира Бакстер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.