Как справиться, когда факториал становится слишком большим для int в С++?

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

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

int factorial(int x)
{
    if (x < 0) error("Can't take factorial of a negative number.");
    else if (x == 0) return 1;
    else return x * factorial(x - 1);
}

Если, например, я вызову функцию с 50, она вернет 0. Я хотел бы выдать сообщение об ошибке в таких случаях. Может ли кто-нибудь исправить меня по этому поводу?

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

Algirdas Preidžius 28.05.2019 16:44

Просто проверьте, не слишком ли большой x. 12! это самый большой факториал, который может быть сохранен в 32-битном целочисленном, поэтому просто проверьте, больше ли x 12.

NathanOliver 28.05.2019 16:44

Чтобы добавить к Натану, вы можете использовать unsigned long long, а затем вы можете хранить до 20!.

Michael Chourdakis 28.05.2019 16:48

Максимальный размер int известен, как и размер long. В последнем случае, вместо того, чтобы немедленно возвращать значение, сохраните его в long, проверьте, больше ли оно, чем int max, и выполните соответствующую ошибку или продолжите рекурсию.

jwatts1980 28.05.2019 16:51

@ jwatts1980 Это будет работать только на 64-битных машинах * Nix. Windows по-прежнему использует 32-битные длины. Также могут быть системы, в которых sizeof(int) == sizeof(long) == sizeof(long long) так что это не будет работать и в этих системах.

NathanOliver 28.05.2019 16:54

@NathanOliver для таких систем все типы будут иметь ширину 64 бита, поэтому в некотором смысле это будет «работать».

Dan M. 28.05.2019 16:56

@ДэнМ. Как это будет «работать»? Результат никогда не будет больше, чем INT_MAX.

NathanOliver 28.05.2019 16:57

@NathanOliver ах, извините, неправильно прочитал комментарий, на который вы отвечали, и предположил, что вы говорили о факториалах, вписывающихся в целые числа. Если хотите, вы можете обнаружить переполнение с беззнаковыми типами, но это не имеет особого смысла.

Dan M. 28.05.2019 17:00

Зачем вообще писать такую ​​дрянную рекурсивную функцию? Есть метод, который разворачивает такие рекурсивные функции в циклы, но ваш этого не сделает.

ALX23z 28.05.2019 17:18
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
9
203
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Факториальная функция — это статическая функция, вывод которой всегда будет одним и тем же при одних и тех же входных данных. Следовательно, мы можем заранее знать, переполнится ли данный вход или нет.

int32_t factorial(int32_t val) {
    if (val > 12)
        throw std::runtime_error("Input too large for Factorial function (val must be less than 13)");
    if (val < 0)
        throw std::runtime_error("Input must be non-negative");
    if (val == 0)
        return 1;
    return factorial(val-1) * val;
}

int64_t factorial(int64_t val) {
    if (val > 20)
        throw std::runtime_error("Input too large for Factorial function (val must be less than 21)");
    if (val < 0)
        throw std::runtime_error("Input must be non-negative");
    if (val == 0)
        return 1;
    return factorial(val-1) * val;
}

Если вместо этого вы хотите динамически обнаруживать целочисленное переполнение, вам необходимо просмотреть строгие методы обнаружения возможного переполнения.

Спасибо, я не знал, что должен просто проверить значение, прежде чем принимать факториал.

Daniel 28.05.2019 17:41

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

Gem Taylor 28.05.2019 17:58

Более общая проверка включает в себя взятие MAX_INT и деление на один из множителей, а затем проверку того, меньше ли результат, чем другой множитель, но это происходит за счет деления.

Gem Taylor 28.05.2019 17:59

@GemTaylor В такой простой функции я верю, что компилятор найдет способ оптимизировать логику.

Xirema 28.05.2019 17:59

Более быстрая проверка включает в себя первую проверку того, пересекают ли два умножаемых значения битовый порог. Количество битов в одном плюс биты в другом < целевого битового размера (31 или 63 для знака) => все хорошо; больше => все плохо; равны, то вам, возможно, придется сделать деление.

Gem Taylor 28.05.2019 18:03

Другие вопросы по теме