Получить количество цифр в беззнаковое длинное целое число с#

Я пытаюсь определить количество цифр в номере c# ulong, я пытаюсь сделать это, используя некоторую математическую логику, а не используя ToString().Длина. Я не сравнивал подходы 2, но видел другие сообщения об использовании системы.Математика.напольная система.Математика.Log10 (number)) + 1 для определения количества цифр. Кажется, работает нормально, пока я не перейду от 99999999999999997 к 999999999999998, и в этот момент я начинаю получать неправильный счет.

кто-нибудь сталкивался с этим проблема раньше ?

Я видел похожие сообщения с акцентом Java @ почему log (1000) / log(10) не совпадает с log10(1000)?, а также пост @ как получить отдельные цифры числа int? что указывает, как я мог бы достичь того же с помощью оператора%, но с гораздо большим количеством кода

вот код, который я использовал для имитации этого

Action<ulong> displayInfo = number => 
 Console.WriteLine("{0,-20} {1,-20} {2,-20} {3,-20} {4,-20}", 
  number, 
  number.ToString().Length, 
  System.Math.Log10(number), 
  System.Math.Floor(System.Math.Log10(number)),
  System.Math.Floor(System.Math.Log10(number)) + 1);

Array.ForEach(new ulong[] {
 9U,
 99U,
 999U,
 9999U,
 99999U,
 999999U,
 9999999U,
 99999999U,
 999999999U,
 9999999999U,
 99999999999U,
 999999999999U,
 9999999999999U,
 99999999999999U,
 999999999999999U,
 9999999999999999U,
 99999999999999999U,
 999999999999999999U,
 9999999999999999999U}, displayInfo);

Array.ForEach(new ulong[] {
 1U,
 19U,
 199U,
 1999U,
 19999U,
 199999U,
 1999999U,
 19999999U,
 199999999U,
 1999999999U,
 19999999999U,
 199999999999U,
 1999999999999U,
 19999999999999U,
 199999999999999U,
 1999999999999999U,
 19999999999999999U,
 199999999999999999U,
 1999999999999999999U
}, displayInfo);

спасибо заранее

Пэт

4 ответов


log10 будет включать преобразование с плавающей запятой-следовательно, ошибка округления. Ошибка довольно мала для double, но это большое дело для точного целого числа!

за исключением .Метод ToString() и метод с плавающей запятой, тогда да, я думаю, вам придется использовать итерационный метод, но я бы использовал целочисленное деление, а не по модулю.

целое деление на 10. Это результат>0? Если это так, повторите. Если нет, остановись. Число цифр - это число требуемых итераций.

например. 5 -> 0; 1 итерация = 1 цифра.

1234 -> 123 -> 12 -> 1 -> 0; 4 итераций = 4 цифры.


Я хотел бы использовать ToString().Length Если вы не знаете, это будет называться миллионы раз.

"преждевременная оптимизация-корень всех зол" - Donald Knuth


С документация:

по умолчанию двойное значение содержит 15 десятичные цифры точности, хотя максимум 17 цифр внутренне.

Я подозреваю, что вы работаете в пределы точности. Ваше значение 999,999,999,999,999,998, вероятно, находится на пределе точности. И с тех пор ulong должен быть преобразован в double перед вызовом Math.Log10, вы видите эту ошибку.


другие ответы выложили почему это произойдет.

вот пример довольно быстрого способа определения "длины" целого числа (некоторые случаи исключены). Это само по себе не очень интересен, но я включаю его здесь, потому что используя этот метод в сочетании с Log10 может получить точность "идеально" для всего диапазона без знака долго, не требуя второго вызова журнала.

// the lookup would only be generated once
// and could be a hard-coded array literal
ulong[] lookup = Enumerable.Range(0, 20)
    .Select((n) => (ulong)Math.Pow(10, n)).ToArray();
ulong x = 999;
int i = 0;
for (; i < lookup.Length; i++) {
    if (lookup[i] > x) {
        break;
    }
}
// i is length of x "in a base-10 string"
// does not work with "0" or negative numbers

этот подход таблицы поиска может быть легко конвертируется в любую базу. этот метод должен быть быстрее, чем итеративный подход деления на базы но профилирование оставлено как упражнение для читателя. (Прямая ветвь if-then, разбитая на" группы", вероятно, еще быстрее, но это слишком много повторяющихся типов на мой вкус.)

удачи в кодировании.