#rust
Вопрос:
Я пытаюсь получить длину (количество цифр при интерпретации в десятичном формате) int
в rust. Я нашел способ сделать это, однако ищу метод, который исходит из самого примитива. Это то, что у меня есть:
let num = 90.to_string();
println!("num: {}", num.chars().count())
// num: 2
Я смотрю на https://docs.rs/digits/0.3.3/digits/struct.Метод Digits.html#.длина. это хороший кандидат? Как мне его использовать? Или есть другие ящики, которые делают это за меня?
Один лайнер с меньшим преобразованием типов-идеальное решение, которое я ищу.
Комментарии:
1. Это
90.to_string().len()
то, что тебе нужно?2. мне нравится этот @hzqelf — идеальным случаем была бы функция, которая не меняет тип на строку
Ответ №1:
Вы можете сделать цикл и проверить, как часто вы можете делить число на 10, прежде чем оно превратится в одну цифру. Или в другом направлении (поскольку деление происходит медленнее, чем умножение), проверьте, как часто вы можете умножать 10*10*...*10
, пока не достигнете нужного числа:
fn length(n: u32, base: u32) -> u32 {
let mut power = base;
let mut count = 1;
while n >= power {
count = 1;
if let Some(new_power) = power.checked_mul(base) {
power = new_power;
} else {
break;
}
}
count
}
С ночной ржавчиной (или в будущем, когда int_log
функция стабилизируется) вы можете использовать:
#![feature(int_log)]
n.checked_log10().unwrap_or(0) 1
Комментарии:
1. я больше искал что-то вроде одного лайнера. Я не могу избавиться от мысли, что такой один лайнер должен существовать :/ . тем не менее, отличный ответ
2. @AthulMuralidhar Мне любопытно, что вы находите полезным с одним лайнером в таком контексте.
3. Не нужно беспокоиться о том, что деление будет медленнее, чем умножение, если вы делите на константу. В любом случае оптимизатор будет представлять деление как умножение на обратное.
4. @пользователь На какую целочисленную константу вы можете умножить a
u32
, чтобы разделить ее на 10?5. Компиляторы иногда преобразуются более агрессивно, чем вы думаете. Оказывается, что моя
length
функция, вызываемая с константой времени компиляции 10, вообще ничего не умножает; она просто превращается в сериюif
операторов
Ответ №2:
Вот однострочный, который не требует строк или плавающей запятой:
println!("num: {}", successors(Some(n), |amp;n| (n >= 10).then(|| n / 10)).count());
Он просто подсчитывает, сколько раз начальное число нужно разделить на 10, чтобы достичь 0.
РЕДАКТИРОВАТЬ: первая версия этого ответа использовалась iterate
из (превосходного и настоятельно рекомендуемого) itertools
ящика, но @trentcl указал, что successors
из stdlib делает то же самое. Для справки, вот версия с использованием iterate
:
println!("num: {}", iterate(n, |amp;n| n / 10).take_while(|amp;n| n > 0).count().max(1));
Комментарии:
1. Вы могли бы сделать то же самое в
std
с.successors
std
-только версия:successors(Some(n), |amp;n| (n >= 10).then(|| n / 10)).count()
(этот комментарий не является советом по программированию)2. @trentcl Приятно, спасибо! На самом деле я искал что-то подобное
successors()
, но не смог найти и остановился наitertools::iterate()
этом .
Ответ №3:
Вот (едва ли) однострочный текст, который быстрее, чем преобразование строк с использованием std::iter:
let some_int = 9834;
let decimal_places = (0..).take_while(|i| 10u64.pow(*i) <= some_int).count();
Комментарии:
1. Аккуратно, но имеет ошибку переполнения для входных данных, превышающих 1 миллиард.
2. @Daniel спасибо, сменил базу на u64
3. Переполнение все еще существует, оно просто перемещено на входы, превышающие или равные 10 квинтиллионам. play.rust-lang.org/…
4. Верно, но если вход остается i/u32, все в порядке.
5. Или нет, это сделало some_int выводимым как u64 (потому что так и должно быть).. черт
Ответ №4:
Первый метод, приведенный ниже, основан на следующей формуле, где a
и b
являются логарифмическими основаниями.
log<a>( x ) = log<b>( x ) / log<b>( a )
log<a>( x ) = log<2>( x ) / log<2>( a ) // Substituting 2 for `b`.
Следующая функция может быть применена для определения количества цифр для оснований, которые имеют степень 2. Этот подход очень быстрый.
fn num_digits_base_pow2(n: u64, b: u32) -> u32
{
(63 - n.leading_zeros()) / (31 - b.leading_zeros()) 1
}
Биты подсчитываются как n
для (числа, которое мы хотим представить), так и b
для (базы), чтобы найти их значения уровня log2. Затем скорректированное соотношение этих значений дает значение журнала потолка в нужной базе.
Для подхода общего назначения к нахождению количества цифр для произвольных оснований должно быть достаточно следующего.
fn num_digits(n: u64, b: u32) -> u32
{
(n as f64).log(b as f64).ceil() as u32
}
Комментарии:
1. С точки зрения производительности, с плавающей запятой
log
-это одна инструкция HW, которая будет быстрее, чем цикл SW. Однако вы можете заменить своиwhile
петли с помощьюleading_zeros
:n_floor_log2 = 32-x.leading_zeros()
Ответ №5:
Хорошее свойство чисел, которое всегда полезно иметь в виду, состоит в том, что количество цифр, необходимых для записи числа $x$ в базе $n$, на самом деле равно $lceil log_n(x 1) rceil$.
Поэтому можно просто написать следующую функцию (обратите внимание на приведение от u32
к f32
, так как целые числа не имеют функции журнала).
fn length(n: u32, base: u32) -> u32 {
let n = (n 1) as f32;
n.log(base as f32).ceil() as u32
}
Вы можете легко адаптировать его для отрицательных чисел. Для чисел с плавающей запятой это может быть немного (т. Е. Намного) сложнее.
Чтобы принять во внимание комментарий Даниэля о патологических случаях, возникающих при использовании f32, обратите внимание, что в случае ночной ржавчины целые числа имеют метод логарифма. (Обратите внимание, что, имо, это детали реализации, и вам следует больше сосредоточиться на понимании алгоритма, чем на реализации.):
#![feature(int_log)]
fn length(n: u32, base: u32) -> u32 {
n.log(base) 1
}
Комментарии:
1. Не все
u32
вписывается вf32
ошибку без округления, поэтому в некоторых случаях ваше решение возвращает неправильный результат, например, длина(0x8000047f, 2) = 31, но длина(0x80000480, 2) = 32. Кроме того, у вас есть ошибка переполнения,(n 1)
сбой дляn=0xffffffff
2. @Daniel Я технически согласен с вами (и я соответствующим образом обновил ответ). Тем не менее, ОП, возможно, захочет взглянуть на восторг Хакера за хорошие математические трюки, подобные этому : en.wikipedia.org/wiki/Hacker’s_Delight
3. Проблема с этими «простыми» подходами заключается в том, что они часто просто не работают. Например, это возвращает 5 как для 9999, так и для 10000.
4. @user4815162342 Я с вами не согласен. Ошибка, о которой вы упомянули, связана только с тем, что я быстро написал пример (и его легко было исправить), а не с фактом глубокой ошибки в рассуждениях. Как правило, подходы, основанные на логарифмировании, потерпят неудачу в случае, если
n = 0
. Да, это известно, и это не важно в ответе . Цель ответов на вопросы состоит в том, чтобы дать подход, а затем OP может интегрировать его в более надежный контекст. Я бы сказал, что логарифмический подход лучше, чем создание строки и подсчет или наличие цикла (за исключением, возможно, в случае базы 2).5. Я прочитал весь пост, спасибо, и нет, добавление второго варианта без ошибок (который работает только для людей по ночам, которые готовы полагаться на нестабильные функции) не устраняет проблемы в первом фрагменте. Если вы пишете код с ошибками, решение состоит в том, чтобы исправить ошибки, а не писать больше кода.