результат выходит за пределы допустимых значений для типа переменной
Вот код
{
unsigned short i, f;
f = 1;
for (i = 2; i <= num; i++)
f = f*i;
return f;
}
Если я ввожу например 8 функция возратит правельное значение, так как оно входит в диапазон unsigned short.
Если я ввожу например 9 будет ошибка результат выходит за пределы допустимых значений для типа.
Как мне сделать, что при ситуации когда результат выходит за пределы допустимых значений для типа, выскакивало бы сообщение например "Факториал не может быть вычеслин".
Помогите пожалуйста.
{
unsigned short i, f;
f = 1;
for (i = 2; i <= num; i++)
{
__asm
{
mov ax, f
mov cx, i
mul cx
jnc __local
jmp __carry
__local: mov f, ax
}
}
return f;
__carry: MessageBox(NULL, "Out of range", NULL, NULL);
return 0;
}
Если извращаться не хочешь ;) , запоминай предыдущее значение f, сравнивай с полученным -- если полученное на очередном шаге меньше предыдущего, то out of range
{
unsigned short i, f, prev_f;
f = 1;
for (i = 2; i <= num; i++)
{
prev_f = f;
f = f * i;
if(prev_f >= f)
{
MessageBox(NULL, "Out of range", NULL, NULL);
return 0;
}
}
return f;
}
try {/*вычисления*/}
catch (...){/*сообщение об исключении*/}
Как мне сделать, что при ситуации когда результат выходит за пределы допустимых значений для типа, выскакивало бы сообщение например "Факториал не может быть вычеслин".
Помогите пожалуйста.
{
if(num > 8) {
throw "Факториал не может быть вычЕслИн"
}
unsigned short i, f;
f = 1;
for (i = 2; i <= num; i++)
f = f*i;
return f;
}
Вот код
unsigned short factorial(unsigned short num);
int main(void)
{
unsigned short numfactorial;
cout << "Enter number factorial: ";
cin >> numfactorial;
unsigned short temp = factorial(numfactorial);
try
{
throw temp;
}
catch (unsigned short temp)
{
cout << "Factorial algoritm: " << temp;
}
catch (...)
{
cout << "Error Factorial!";
}
return 0;
}
unsigned short factorial(unsigned short num)
{
unsigned short result = 1;
for (unsigned short i = 2; i <= num; i++)
result = result*i;
return result;
}
Я ввожу 9, а программа пишет 35200, а должна писать "Error Factorial!"
Подскажите пожалуйста, где я ошибся.
#define _FACTORIAL_H_
class Factorial
{
private:
int num;
public:
Factorial();
~Factorial();
unsigned int recfactorial(unsigned short num);
unsigned int factorial(unsigned short num);
};
Factorial :: Factorial()
{
num = 0;
}
Factorial :: ~Factorial()
{
}
unsigned int Factorial :: recfactorial(unsigned short num)
{
unsigned short result;
if (num == 1 || num == 0)
result = 1;
else
result = num*recfactorial(num-1);
return result;
}
unsigned int Factorial :: factorial(unsigned short num)
{
typedef unsigned short mytype;
unsigned int result = 1;
for (unsigned short i = 2; i <= num; i++)
{
result = result*i;
if (result != mytype(result))
throw "Error Factorial!";
}
return result;
}
#endif
и
#include "factorial.h"
int main(void)
{
unsigned short numfactorial;
cout << "Enter number factorial: ";
cin >> numfactorial;
Factorial Factorial1;
unsigned short temp = 0 ;
try
{
temp = Factorial1.factorial(numfactorial);
}
catch (const char *)
{
cout << "Error Factorial!";
}
if (temp > 0)
cout << "Factorial (not recursiv algoritm): " << temp;
return 0;
}
Работает.
Только у меня один маленький вопросик.
Хотелось бы уточнить одну вещь результат факториала лежит в переменной типа unsigned int, вводимое значение имеет тип unsigned short. Если я введу самое большое значение которое можно ввести unsigned short это 65535, значение фактариала не выдет за значение типа, unsigned int или выдет? Может надо указать другой тип?
С единственным и НЕИСПОЛЬЗУЕМЫМ полем!
...
unsigned short factorial(unsigned short num)
{
unsigned short i, f;
f = 1;
try{
for (i = 2; i <= num; i++)
f = boost::numeric_cast<short>(f*i);
}
catch(boost::numeric::bad_numeric_cast& e)
{
std::cout << "Error in factorial: " << e.what() << std::endl;
return 0; // нет такого числа, факториал которого равен нулю (ну или сделай то, что считаешь нужным)
}
return f;
}
boost::numeric_cast
...
unsigned short factorial(unsigned short num)
{
unsigned short i, f;
f = 1;
try{
for (i = 2; i <= num; i++)
f = boost::numeric_cast<short>(f*i);
}
catch(boost::numeric::bad_numeric_cast& e)
{
std::cout << "Error in factorial: " << e.what() << std::endl;
return 0; // нет такого числа, факториал которого равен нулю (ну или сделай то, что считаешь нужным)
}
return f;
}
boost::numeric_cast
Ещё один пример того, как простые вещи делаются через сложную задницу.
имхо, нет ничего хуже изобретения велосипеда, тогда как есть стандартные решения для стандартных задач.
{
if(num > 8) {
throw "Факториал не может быть вычЕслИн"
}
unsigned short i, f;
f = 1;
for (i = 2; i <= num; i++)
f = f*i;
return f;
}
Есть повод гордиться расширяемостью и легкостью дальнейшего сопровождения.
имхо, нет ничего хуже изобретения велосипеда, тогда как есть стандартные решения для стандартных задач.
Нет ничего хуже применения стандартных решений не по месту и усложнение кода без необходимости. Вот это настоящее зло.
Есть повод гордиться расширяемостью и легкостью дальнейшего сопровождения.
Поговорим о расширяемости и сопровождении варианта с numeric_cast?
TFactorialResult factorial( const unsigned short num )
{
TFactorialResult result = 1;
for( unsigned short i = 1; i < num; ++i ) {
TFactorialResult currentStepResult = result * i;
if( currentStepResult < result ) {
throw "Error";
}
result = currentStepResult;
}
return result;
}
Этот код тоже ошибочен - нельзя быть уверенным, что результат переполнения не окажется в интервале [result;UINT_MAX] для какого-нибудь шага итерации. Как вариант уменьшения вероятности этого можно подсчет вести с конца, а не начала. Для unsigned short его использовать можно :)
Другой, более правильный, но менее быстрый способ - на каждом шаге делать проверку
В принципе для unsigned short накладные расходы будут невелики, но код - более надежным. Будет и более безопасна замена на unsigned int, на __uint64 или на <что-там-будет-дальше>.
Проблема яйца выеденного не стоит, а шуму...
Что касается подхода вообще, то проще проверить входные значения, если функция имеет монотонность и не имеет разрывов (как в случае с факториалом), чем на каждом шаге (ну нифига себе) проверять не вышли ли мы за границы.
R factorial(A arg)
{
R f = 1;
for (A i = 2; i <= arg; ++i)
f *= i;
return f;
}
Какие тут проверки входного значения будем ставить?
R factorial(A arg)
{
R f = 1;
for (A i = 2; i <= arg; ++i)
f *= i;
return f;
}
Совсем невероятно. Но когда такая необходимость появится, тогда и будем её решать.
Ты читал Джоэла Спольски? У него есть хорошая заметка, которая подойдет и к твоему посту:
http://www.joelonsoftware.com/articles/fog0000000018.html
Какие тут проверки входного значения будем ставить?
Так вот, если необходимость появится, проще, опять же, проверить соответствие входных данных разрядности типа результата. Причем можно так же использовать компилед-тайм, а можно и в ран-тайме с помощью того факта, что log(n!) = log(1) + log(2) + ... + log(n).
Другой вариант - проверять промежуточные значения вычисления на непревышение максимально возможного значения, но он мне нравится меньше.
А теперь покажи, пожалуйста, как ты собираешься решать эту же задачу с помощью boost::numeric_cast ? :)
Я архитектурный астронавт? ну уж нет - это это точно не про меня :)
Совсем невероятно. Но когда такая необходимость появится, тогда и будем её решать.
Почему невероятно? Я часто пишу сначала нешаблонную функцию для того, чтобы проверить алгоритм, а потом её обощаю для удобства дальнейшего использования.
Бывает полезно подумать о наиболее вероятных изменениях, которые захочется позже сделать. ИМХО, переделывать функции на шаблонные - весьма вероятное изменение.
Как с numeric_cast? Ну так весь прикол в том, что практически точно также:
R factorial(A arg)
{
R f = 1;
try{
for (A i = 2; i <= arg; ++i)
f = boost::numeric_cast<R>(f*i);
}
catch(boost::numeric::bad_numeric_cast& e)
{
std::cout << "Error in factorial: " << e.what() << std::endl;
return 0;
}
return f;
}
Или вообще так: (если потом не лениться ловить у себя в коде исключения)
R factorial(A arg)
{
R f = 1;
for (A i = 2; i <= arg; ++i)
f = boost::numeric_cast<R>(f*i);
return f;
}
Единственный минус этого решения я вижу в том, что клиентам этой функции прийдётся явно указывать тип возвращаемого значения.
Разрабатываю на C# (в настоящий момент веб проект с самописным ORM). Сколько шаблонных функций пришлось мне реализовать? Одну. Для постраничного чтения запроса в БД. В остальном кое где отражение, кое где рантаймное кодогенерирование.
Как с numeric_cast? Ну так весь прикол в том, что практически точно также:
R factorial(A arg)
{
R f = 1;
try{
for (A i = 2; i <= arg; ++i)
f = boost::numeric_cast<R>(f*i);
}
catch(boost::numeric::bad_numeric_cast& e)
{
std::cout << "Error in factorial: " << e.what() << std::endl;
return 0;
}
return f;
}
Минут 5 вникал чего делается тут.
Зачем тут нумерик каст?
С каких это пор факторил будет вычисляться для дофигища типов чисел. Вещественные вычисления существеннее медленнее целочисленных. Тогда какой смысл нам использовать их для малых значений факториала?
Гораздо удобнее использовать наиболее широкий целочисленный тип наподобие int64 либо вообще свести к табличному поиску значений функции. Факториал - он вроде не меняется у нас на протяжении пары сотен лет.
Это к вопросу о неиспользовании существующих решений? Неиспользование NHiberbante, например?
Сколько шаблонных функций пришлось мне реализовать? Одну. Для постраничного чтения запроса в БД. В остальном кое где отражение, кое где рантаймное кодогенерирование.
Похожий опыт. 98% нужды в generic types покрывается типизированными коллекциями.
Зачем тут нумерик каст?
С каких это пор факторил будет вычисляться для дофигища типов чисел. Вещественные вычисления существеннее медленнее целочисленных. Тогда какой смысл нам использовать их для малых значений факториала?
Гораздо удобнее использовать наиболее широкий целочисленный тип наподобие int64 либо вообще свести к табличному поиску значений функции. Факториал - он вроде не меняется у нас на протяжении пары сотен лет.
Да чего тут обсуждать. Для нецелых чисел факториал не определен, для целых стандартых типов - настолько тривиальная функция, что и не знаю чего тут можно обсуждать. Я еще понимаю - вычисление факториала от на длинной арифметике. Но даже и в этом случае сводится к элементарным операциями умножения длинных чисел.
Я архитектурный астронавт? ну уж нет - это это точно не про меня :)
Не стоит все трактовать слишком буквально.
Я привел ссылку на эту статью потому, что её смысл в том, что не стоит увлекаться обобщениями и абстракциями. Нужно в первую очередь решить конкретную задачу, и очень часто этого бывает достаточно.
Почему невероятно? Я часто пишу сначала нешаблонную функцию для того, чтобы проверить алгоритм, а потом её обощаю для удобства дальнейшего использования.
Бывает полезно подумать о наиболее вероятных изменениях, которые захочется позже сделать. ИМХО, переделывать функции на шаблонные - весьма вероятное изменение.
Подумать полезно, реализовывать задуманное без необходимости - нет.
Как с numeric_cast? Ну так весь прикол в том, что практически точно также:
R factorial(A arg)
{
R f = 1;
try{
for (A i = 2; i <= arg; ++i)
f = boost::numeric_cast<R>(f*i);
}
catch(boost::numeric::bad_numeric_cast& e)
{
std::cout << "Error in factorial: " << e.what() << std::endl;
return 0;
}
return f;
}
Или вообще так: (если потом не лениться ловить у себя в коде исключения)
R factorial(A arg)
{
R f = 1;
for (A i = 2; i <= arg; ++i)
f = boost::numeric_cast<R>(f*i);
return f;
}
Единственный минус этого решения я вижу в том, что клиентам этой функции прийдётся явно указывать тип возвращаемого значения.
Ну так весь прикол в том, что этот код неработоспособный. В этом же его основной минус.
Тест №1:
Вот уже не думал, что ответом будет 0.
Тест №2:
Ну уж точно не подумал бы, что ответом будет -2147483648.
Ты же хотел решить обобщенную задачу, не оговаривая рамок этой обобщенности, поэтому ...
Тест №3:
Ответ 2 ???
Даже google знает, что это не так:
http://www.google.ru/search?aq=-1&oq=&complete=1&hl=ru&newwindow=1&q=2.5%21
Просто продемонстировал, что размер проекта достаточный для оценки.
Шеф сказал без NHibernate - я организовал архитектуру без NHibernate. Неплохо получилось :) - задачу система решает в разумных масштабах. Кстати одна из фишек - интеграция вызова хранимых процедур в C#: от программиста требуется только прототип объявить.