Форум программистов, компьютерный форум CyberForum.ru

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
taras atavin
Ушёл с форума.
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
#1

Дробь - C++

23.11.2011, 08:53. Просмотров 757. Ответов 14
Метки нет (Все метки)

Нужен шаблон класса "дробь" с именем TFraction, подобный обыкновенной дроби в том, что данная TFraction должен иметь числитель и знаменатель, но обе эти его части могут быть любых встроенных числовых типов, включая действительные, а не только целыми (то есть допускается специализация на float для хранения дробей вроде http://www.cyberforum.ru/cgi-bin/latex.cgi?\frac{25.4f}{0.01f}). Требуется поддерживать операции: +, -, *, /, +=, -=, *=, /= без приведения объектов к встроенным числам и с максимальным сохранением точности, причём, операции *= и /= должны поддерживать и правые операнды встроенных числовых типов, а операции * и / - как правые, так и левые операнды встроенных числовых типов, но один операнд обязательно должен быть экземпляром TFraction, а в операциях +, -, += и -= оба операнда должны быть экземплярами TFraction. Требуется также поддерживать приведение TFraction ко встроенным числовым типам. У меня не получается придумать иные реализации арифметических операторов, кроме:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
TFraction TFraction::operator *(TFraction &x)
{
 TFraction Result;
 Result.Numerator  =Numerator    *x.Numerator;
 Result.Denominator=Denominator*x.Denominator;
 return Result;
}
TFraction TFraction::operator /(TFraction &x)
{
 TFraction Result;
 Result.Numerator  =Numerator    *x.Denominator;
 Result.Denominator=Denominator*x.Numerator;
 return Result;
}
TFraction::operator *=(TFraction &x)
{
 Numerator   *=x.Numerator;
 Denominator*=x.Denominator;
 return *this;
}
TFraction TFraction::operator /=(TFraction &x)
{
 Numerator   *=x.Denominator;
 Denominator*=x.Numerator;
 return *this;
}
TFraction::operator *(double &x)
{
 TFraction Result;
 Result.Numerator  =Numerator    *x;
 Result.Denominator=Denominator;
 return Result;
}
TFraction TFraction::operator /(double &x)
{
 TFraction Result;
 Result.Numerator  =Numerator
 Result.Denominator=Denominator*x;
 return Result;
}
TFraction TFraction::operator *=(double &x)
{
 Numerator*=x;
 return *this;
}
TFraction TFraction::operator /=(double &x)
{
 Denominator*=x;
 return *this;
}
TFraction TFraction::operator +(TFraction &x)
{
 TFraction Result;
 Result.Numerator  =Numerator*x.Denominator+xNumerator*Denominator;
 Result.Denominator=Denominator*x.Denominator;
 return Result;
}
TFraction TFraction::operator -(TFraction &x)
{
 TFraction Result;
 Result.Numerator  =Numerator*x.Denominator-xNumerator*Denominator;
 Result.Denominator=Denominator*x.Denominator;
 return Result;
}
TFraction TFraction::operator +=(TFraction &x)
{
 TFraction Buffer;
 Buffer.Numerator  =Numerator*x.Denominator+xNumerator*Denominator;
 Buffer.Denominator=Denominator*x.Denominator;
 *this=Buffer;
 return *this;
}
TFraction TFraction::operator -=(TFraction &x)
{
 TFraction Buffer;
 Buffer.Numerator  =Numerator*x.Denominator-xNumerator*Denominator;
 Buffer.Denominator=Denominator*x.Denominator;
 *this=Buffer;
 return *this;
}
TFraction::operator double ()
{
 return Numerator/Denominator;
}
friend TFraction operator *(double &x, TFraction &y)
{
 TFraction Result;
 Result.Numerator  =x*y.Numerator;
 Result.Denominator=  y.Denominator;
 return Result;
}
TFraction TFraction::operator /(double &x)
{
 TFraction Result;
 Result.Numerator  =  y.Numerator;
 Result.Denominator=x*y.Denominator;
 return Result;
}
и перегрузки тех операторов, которые с double путём замены double на другие числовые типы. Но я сомневаюсь в оптимальности данного исходника в плане точности и чего то не соображу, как избавиться от временных объектов в операторах с =. С такой оптимизацией у меня проблемы не меньшие, чем у новичков. Поможите?
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Bers
Заблокирован
23.11.2011, 08:59     Дробь #2
Приведите весь код (хэдэр/реализация)
taras atavin
Ушёл с форума.
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
23.11.2011, 09:17  [ТС]     Дробь #3
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
template <class TNumerator, class TDenominator> class TFraction
{
 public:
  TNumerator    Numerator;
  TDenominator Denominator;
  TFraction operator *(TFraction &x);
  TFraction operator /(TFraction &x);
  TFraction operator +(TFraction &x);
  TFraction operator -(TFraction &x);
  TFraction operator *=(TFraction &x);
  TFraction operator /=(TFraction &x);
  TFraction operator +=(TFraction &x);
  TFraction operator +=(TFraction &x);
  TFraction operator ==(TFraction &x);
  TFraction operator !=(TFraction &x);
               operator double ();
               operator float ();
               operator int ();
               operator __int64 ();
  TFraction operator *(double &x);
  TFraction operator /(double &x);
  TFraction operator *=(double &x);
  TFraction operator /=(double &x);
  friend TFraction operator *(double &x TFraction &y);
  friend TFraction operator /(double &x TFraction &y);
  TFraction operator *(float &x);
  TFraction operator /(float &x);
  TFraction operator *=(float &x);
  TFraction operator /=(float &x);
  friend TFraction operator *(float &x TFraction &y);
  friend TFraction operator /(float &x TFraction &y);
  TFraction operator *(int &x);
  TFraction operator /(int &x);
  TFraction operator *=(int &x);
  TFraction operator /=(int &x);
  friend TFraction operator *(int &x TFraction &y);
  friend TFraction operator /(int &x TFraction &y);
  TFraction operator *(__int64 &x);
  TFraction operator /(__int64 &x);
  TFraction operator *=(__int64 &x);
  TFraction operator /=(__int64 &x);
  friend TFraction operator *(__int64 &x TFraction &y);
  friend TFraction operator /(__int64 &x TFraction &y);
};
Добавлено через 1 минуту
Я хорошо оптимизирую только СЛАУ, алгоритмическая оптимизация всего остального не получается вообще, обычная диметрия графика функции двух аргументов строилась 23 минуты на спектруме, да и на сотом пне - более 18-ти минут.
Bers
Заблокирован
23.11.2011, 09:27     Дробь #4
ну первое, что в глаза бросается:

C++
1
2
3
4
5
6
7
TFraction TFraction::operator +(TFraction &x)
{
*TFraction Result;
*Result.Numerator *=Numerator*x.Denominator+xNumerator*Denominator;
*Result.Denominator=Denominator*x.Denominator;
*return Result;
}
отсутствие константности источника, и выдача наружу данных по значению (копирование будит)

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

Третие: внутри функции зачем то создаётся временный объект...

В общем, я не совсем понимаю логику. Какую задачу должен решать класс?
taras atavin
Ушёл с форума.
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
23.11.2011, 09:33  [ТС]     Дробь #5
К тому же за исключением операторов +=, -=, /= и *= задача - оптимизировать только точность, но не время, за что я вообще ни когда не брался.

Добавлено через 1 минуту
Цитата Сообщение от Bers Посмотреть сообщение
*TFraction Result;
Звездочка перед типом? Так можно? А что она в том случае означает?
Bers
Заблокирован
23.11.2011, 09:35     Дробь #6
taras atavin, не понял насчет звездочки)

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

Имейте ввиду, шаблон - такая штука. В качестве числителя и знаменателя могут скормить что очень тяжелое.

Шаблон - всегда многоразовый код
taras atavin
Ушёл с форума.
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
23.11.2011, 09:35  [ТС]     Дробь #7
Цитата Сообщение от Bers Посмотреть сообщение
А второе: У вас числитель и знаменатель могут быть кем угодно, и поэтому, классу легко по ничайности скормить несовместимые типы, на что получатся очень невразумительные ошибки компиляции.
Как от этого защититься? Как сделать, чтоб числитель и знаменатель могли быть любых числовых, но только числовых типов?
Bers
Заблокирован
23.11.2011, 09:38     Дробь #8
Цитата Сообщение от taras atavin Посмотреть сообщение
Как от этого защититься? Как сделать, чтоб числитель и знаменатель могли быть любых числовых, но только числовых типов?
Способов много всяких. Тут главное понять - какие типы могут быть параметрами шаблона, а какие не могут.
taras atavin
Ушёл с форума.
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
23.11.2011, 09:55  [ТС]     Дробь #9
Цитата Сообщение от Bers Посмотреть сообщение
Какую задачу должен решать класс?
Ну вот представь: (1/3)*(2/7). Даблом и треть то нельзя точно представить, а при умножении наложатся три ошибки округления: обоих операндов и результата. А если обыкновенной дробью, то можно округлить только результат. То есть уже 2.0/27.0 точнее, чем (1.0/3.0)*(2.0/7.0) с явным запоминанием значений обоих множителей и без какой либо оптимизации. А если множителей больше? Если (254/10)*(5/3)*(6/7)*(4/13)? И выбор - пять ошибок округления, или одна? При этом я не хочу тупо наращивать мантиссу, так как по мере дальнейшего роста длины выражения ошибка всё равно накопится. Числитель и знаменатель одного типа = двойная разрядность. Но если множителей в цепочке несколько тысяч, то что будет точнее: TFraction двойной разядности, или подобие плавающая запятая тройой разядности?

Добавлено через 30 секунд
Цитата Сообщение от Bers Посмотреть сообщение
. Тут главное понять - какие типы могут быть параметрами шаблона, а какие не могут.
Числовые могут, остальные - нет. И явное перечисление допустимых типов шаблона как то не впечатляет.

Добавлено через 4 минуты
Цитата Сообщение от Bers Посмотреть сообщение
копирование буди
Пусть будет, так задумано.

Добавлено через 1 минуту
Цитата Сообщение от Bers Посмотреть сообщение
не понял насчет звездочки)
Смотри свой пост, строка № 4 начинается со звёздочки, потом написан тип.
Bers
Заблокирован
23.11.2011, 09:56     Дробь #10
Цитата Сообщение от taras atavin Посмотреть сообщение
Числовые могут, остальные - нет. И простое перечисление допустимых типов шаблона как то не впечатляет.
Я щас не скажу на вскидку, но Александресску и тп - все они провозглашали компиле-тайм валидаторы на POD_объекты. Или как отличить int от пользовательского типа.


С другой стороны, double вместит в себя все возможные темы связанные с обработкой чисел.

А ещё не понятно, почему твой класс обрабатывае чужие данные ((создаёт временные объекты) вместо своих.


Я б сделал так: Прописал в документации : только для числовых типов, либо под свой страх и риск.

Далее: Числителем и знаменателем могут быть типы самого шаблона.

Ну то есть, я объявил: Дробь<int, int> a
Дробь<int, int> b

А потом : Дробь <Дробь, Дробь> (a,b);

Экземпляр обрабатывает только свои данные. Никаких копий.
taras atavin
Ушёл с форума.
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
23.11.2011, 09:57  [ТС]     Дробь #11
Цитата Сообщение от Bers Посмотреть сообщение
Третие: внутри функции зачем то создаётся временный объект...
Ну это же operator +, он должен вернуть сумму, не меняя ни один из своих операндов. Здесь пусть будет через временный объект, а вот как от него избавиться в operator +=?
Bers
Заблокирован
23.11.2011, 09:58     Дробь #12
Цитата Сообщение от taras atavin Посмотреть сообщение
Смотри свой пост, строка № 4 начинается со звёздочки, потом написан тип.
это косяк местного форума. Он звездочки иногда втыкает
taras atavin
Ушёл с форума.
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
23.11.2011, 10:04  [ТС]     Дробь #13
Цитата Сообщение от Bers Посмотреть сообщение
Далее: Числителем и знаменателем могут быть типы самого шаблона.
Четыхэтажная дробь? Зачем? Любой четырёхэатажник приводится к двум этажам переворачиванием знаменателя с превращением его в множитель.

Добавлено через 45 секунд
Цитата Сообщение от Bers Посмотреть сообщение
Экземпляр обрабатывает только свои данные. Никаких копий.
Каким образом? Не понятно.

Добавлено через 1 минуту
По времени оптимизировать только +=, -=, *= и /=, остальные только по точности. +=, -=, *=, /= по времени и точности, но важнее точность. Любые тормоза в замен ещё большей точности считать оправданными.
Bers
Заблокирован
23.11.2011, 10:05     Дробь #14
Цитата Сообщение от taras atavin Посмотреть сообщение
Каким образом? Не понятно.
C++
1
2
3
4
5
6
template<class TypeT>
class TTest
{
    int a;
public:  TTest& operator +(const TTest<TypeT>& istok)  {  a=a+istok.a; return *this; }
};
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
24.11.2011, 06:46     Дробь
Еще ссылки по теме:

C++ Преобразовать дробь в валюту
Класс Рациональная дробь C++
Рациональная дробь C++
C++ Необходимо сократить дробь
C++ Бесконечна ли дробь

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
taras atavin
Ушёл с форума.
3569 / 1752 / 91
Регистрация: 24.11.2009
Сообщений: 27,619
24.11.2011, 06:46  [ТС]     Дробь #15
Цитата Сообщение от Bers Посмотреть сообщение
public: *TTest& operator +(const TTest<TypeT>& istok) *{ *a=a+istok.a; return *this; }
Так ты не решаешь задачу:
C++
1
2
3
TTest a, b, c;
/*Пусть a.a=1, b.a=2*/
c=a+b; /*a.a=3, b.a=2, c.a=3, а надо: a.a=1, b.a=2, c.a=3*/
Добавлено через 8 минут
И для одного поля и так не проблема избавиться от временных объектов в +=, -=, *= и /=. И даже при однократном на один вызов оператора обращении к нескольким полям. Но мне надо изменить знаменатель, а в числителе использовать его старое значение. А, кажется понял. Кажется понял. Надо использовать порядок расчёта числителя и знаменателя: сначала числитель, так как при этом не меняется знаменатель, а потом уже знаменатель:
C++
1
2
3
4
5
6
7
8
9
10
11
12
TFraction TFraction::operator +=(TFraction &x)
{
 Numerator   =Numerator   *x.Denominator+x.Numerator*Denominator;
 Denominator=Denominator*x.Denominator;
 return *this;
}
TFraction TFraction::operator -=(TFraction &x)
{
 Numerator   =Numerator   *x.Denominator-x.Numerator*Denominator;
 Denominator=Denominator*x.Denominator;
 return *this;
}
.

Добавлено через 3 минуты
Всё, оптимизацией по времени больше не занимаемся. Теперь только по точности. Пусть будет возврат временного объекта по значению, то есть копирование. Пусть будет создание временного объекта. Пусть будут тормоза. С ними больше не надо бороться, вся оптимизация должна быть направлена только на точность. Можно целую миллисекунды потратить на однократное исполнение одного оператора-члена, но важен каждый лишний бит, который удастся из сомнительных перетащить в точные.
Yandex
Объявления
24.11.2011, 06:46     Дробь
Ответ Создать тему
Опции темы

Текущее время: 11:57. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru