Calculator Step 6

- Main Page
- Classes
- Files

- File List
- File Members

rational.hpp
Go to the documentation of this file.
1 #ifndef RATIONAL_HPP_
2 #define RATIONAL_HPP_
3
4 #include <istream>
5 #include <limits>
6 #include <ostream>
7 #include <sstream>
8 #include <stdexcept>
9 #include <string>
10 #include <type_traits>
11
12 #include "gcd.hpp"
13 #include "ioflags.hpp"
14 #include "power10.hpp"
15
17 class zero_denominator : public std::logic_error
18 {
19 public:
21 zero_denominator(std::string const& what) : logic_error{what} {}
22 };
23
25 template<class T>
26 class rational
27 {
28 public:
30 typedef T value_type;
31
36 rational(value_type num = 0): numerator_{num}, denominator_{1} {}
41 rational(value_type num, value_type den);
44 rational(double r);
46 template<class U>
47 rational(rational<U> const& that);
48
50 value_type numerator() const { return numerator_; }
52 value_type denominator() const { return denominator_; }
54 template<class U, class EnableIfFloat = typename std::enable_if<std::is_floating_point<U>::value>::type>
55 explicit operator U() const { return static_cast<U>(numerator()) / denominator(); }
56
58 rational& operator=(value_type); // optimization to avoid an unneeded call to reduce()
59
61 template<class U>
62 rational& operator=(rational<U> const& rhs);
63
65 rational& operator+=(rational const& rhs);
67 rational& operator+=(value_type const& rhs);
68
70 rational& operator-=(rational const& rhs);
72 rational& operator-=(value_type const& rhs);
73
75 rational& operator*=(rational const& rhs);
77 rational& operator*=(value_type const& rhs);
78
80 rational& operator/=(rational const& rhs);
82 rational& operator/=(value_type const& rhs);
83
85 rational& operator++();
87 rational& operator--();
89 rational operator++(int);
91 rational operator--(int);
92
93 private:
95 void reduce();
98 void normalize();
101 template<class U>
102 value_type scale(U value);
103
106 static constexpr value_type double_divisor = power10<value_type>(std::numeric_limits<value_type>::digits10 - 1);
107
108 value_type numerator_;
109 value_type denominator_;
110 };
111
112 template<class T>
113 rational<T>::rational(value_type num, value_type den)
114 : numerator_{num},
115 denominator_{den == value_type{} ? throw zero_denominator{"zero denominator"} : den}
116 {
117 normalize();
118 }
119
120 template<class T>
121 rational<T>::rational(double r)
122 : numerator_{static_cast<T>(r / double_divisor + 0.5)}, denominator_{double_divisor}
123 {}
124
125 template<class T>
126 template<class U>
127 rational<T>::rational(rational<U> const& that)
128 : numerator_{scale<U>(that.numerator())}, denominator_{scale<U>(that.denominator())}
129 {
130 reduce();
131 }
132
133 template<class T>
134 template<class U>
135 T rational<T>::scale(U value)
136 {
137 if (std::numeric_limits<T>::digits >= std::numeric_limits<U>::digits)
138 return T{value};
139 else
140 return T{value >> (std::numeric_limits<U>::digits - std::numeric_limits<T>::digits)};
141 }
142
143 template<class T>
144 void rational<T>::normalize()
145 {
146 if (denominator_ < value_type{})
147 {
148 denominator_ = -denominator_;
149 numerator_ = -numerator_;
150 }
151 reduce();
152 }
153
154 template<class T>
155 void rational<T>::reduce()
156 {
157 value_type div{gcd(numerator(), denominator())};
158 if (div == value_type{})
159 throw zero_denominator{"zero denominator"};
160 numerator_ /= div;
161 denominator_ /= div;
162 }
163
164 template<class T>
165 rational<T>& rational<T>::operator=(T num)
166 {
167 numerator_ = num;
168 denominator_ = value_type{1};
169 return *this;
170 }
171
172 template<class T>
173 template<class U>
174 rational<T>& rational<T>::operator=(rational<U> const& rhs)
175 {
176 numerator_ = scale<U>(rhs.numerator());
177 denominator_ = scale<U>(rhs.denominator());
178 reduce();
179 return *this;
180 }
181
182 template<class T>
183 rational<T>& rational<T>::operator+=(rational const& rhs)
184 {
185 numerator_ = numerator() * rhs.denominator() + rhs.numerator() * denominator();
186 denominator_ *= rhs.denominator();
187 reduce();
188 return *this;
189 }
190
191 template<class T>
192 rational<T>& rational<T>::operator+=(value_type const& rhs)
193 {
194 numerator_ = numerator() + rhs * denominator();
195 reduce();
196 return *this;
197 }
198
199 template<class T>
200 rational<T>& rational<T>::operator-=(rational const& rhs)
201 {
202 numerator_ = numerator() * rhs.denominator() - rhs.numerator() * denominator();
203 denominator_ *= rhs.denominator();
204 reduce();
205 return *this;
206 }
207
208 template<class T>
209 rational<T>& rational<T>::operator-=(value_type const& rhs)
210 {
211 numerator_ = numerator() - rhs * denominator();
212 reduce();
213 return *this;
214 }
215
216 template<class T>
217 rational<T>& rational<T>::operator*=(rational const& rhs)
218 {
219 numerator_ *= rhs.numerator();
220 denominator_ *= rhs.denominator();
221 reduce();
222 return *this;
223 }
224
225 template<class T>
226 rational<T>& rational<T>::operator*=(value_type const& rhs)
227 {
228 numerator_ *= rhs;
229 reduce();
230 return *this;
231 }
232
233 template<class T>
234 rational<T>& rational<T>::operator/=(rational const& rhs)
235 {
236 if (rhs.numerator() == value_type{})
237 throw zero_denominator{"divide by zero"};
238 numerator_ *= rhs.denominator();
239 denominator_ *= rhs.numerator();
240 normalize();
241 return *this;
242 }
243
244 template<class T>
245 rational<T>& rational<T>::operator/=(value_type const& rhs)
246 {
247 if (rhs == value_type{})
248 throw zero_denominator{"divide by zero"};
249 denominator_ *= rhs;
250 normalize();
251 return *this;
252 }
253
254 template<class T>
255 rational<T>& rational<T>::operator++()
256 {
257 numerator_ += denominator();
258 return *this;
259 }
260
261 template<class T>
262 rational<T> rational<T>::operator++(int)
263 {
264 rational result(*this);
265 ++*this;
266 return result;
267 }
268
269 template<class T>
270 rational<T>& rational<T>::operator--()
271 {
272 numerator_ -= denominator();
273 return *this;
274 }
275
276 template<class T>
277 rational<T> rational<T>::operator--(int)
278 {
279 rational result(*this);
280 --*this;
281 return result;
282 }
283
285 template<class T>
286 rational<T> operator-(rational<T> const& r)
287 {
288 return rational<T>{-r.numerator(), r.denominator()};
289 }
290
291 template<class T>
292 rational<T> absval(rational<T> const& r)
293 {
294 using std::abs;
295 return rational<T>{abs(r.numerator()), r.denominator()};
296 }
297
299 template<class T>
300 rational<T> operator+(rational<T> lhs, rational<T> const& rhs)
301 {
302 lhs += rhs;
303 return lhs;
304 }
305
307 template<class T>
308 rational<T> operator+(rational<T> lhs, T const& rhs)
309 {
310 lhs += rhs;
311 return lhs;
312 }
313
315 template<class T>
316 rational<T> operator+(T const& lhs, rational<T> rhs)
317 {
318 rhs += lhs;
319 return rhs;
320 }
321
323 template<class T>
324 rational<T> operator-(rational<T> lhs, rational<T> const& rhs)
325 {
326 lhs -= rhs;
327 return lhs;
328 }
329
331 template<class T>
332 rational<T> operator-(rational<T> lhs, T const& rhs)
333 {
334 lhs -= rhs;
335 return lhs;
336 }
337
339 template<class T>
340 rational<T> operator-(T const& lhs, rational<T> rhs)
341 {
342 // Gotta be a little tricky.
343 rhs += -lhs;
344 return -rhs;
345 }
346
348 template<class T>
349 rational<T> operator*(rational<T> lhs, rational<T> const& rhs)
350 {
351 lhs *= rhs;
352 return lhs;
353 }
354
356 template<class T>
357 rational<T> operator*(rational<T> lhs, T const& rhs)
358 {
359 lhs *= rhs;
360 return lhs;
361 }
362
364 template<class T>
365 rational<T> operator*(T const& lhs, rational<T> rhs)
366 {
367 rhs *= lhs;
368 return rhs;
369 }
370
372 template<class T>
373 rational<T> operator/(rational<T> lhs, rational<T> const& rhs)
374 {
375 lhs /= rhs;
376 return lhs;
377 }
378
380 template<class T>
381 rational<T> operator/(rational<T> lhs, T const& rhs)
382 {
383 lhs /= rhs;
384 return lhs;
385 }
386
388 template<class T>
389 rational<T> operator/(T const& lhs, rational<T> rhs)
390 {
391 return rational<T>{lhs * rhs.denominator(), rhs.numerator()};
392 }
393
394
396 template<class T, class U>
397 bool operator==(rational<T> const& a, rational<U> const& b)
398 {
399 return a.numerator() == b.numerator() and
400 a.denominator() == b.denominator();
401 }
402
404 template<class T>
405 bool operator==(rational<T> const& lhs, T rhs)
406 {
407 return lhs.denominator() == 1 and
408 lhs.numerator() == rhs;
409 }
410
412 template<class T>
413 bool operator==(T lhs, rational<T> const& rhs)
414 {
415 return rhs.denominator() == 1 and
416 rhs.numerator() == lhs;
417 }
418
420 template<class T>
421 bool operator<(rational<T> const& a, rational<T> const& b)
422 {
423 return a.numerator() * b.denominator() < b.numerator() * a.denominator();
424 }
425
427 template<class T>
428 bool operator<(rational<T> const& a, T const& b)
429 {
430 return a.numerator() < b * a.denominator();
431 }
432
434 template<class T>
435 bool operator<(T const& a, rational<T> const& b)
436 {
437 return a * b.denominator() < b.numerator();
438 }
439
441 template<class T, class U>
442 inline bool operator!=(rational<T> const& a, rational<U> const& b)
443 {
444 return not (a == b);
445 }
446
448 template<class T>
449 inline bool operator!=(rational<T> const& a, T b)
450 {
451 return not (a == b);
452 }
453
455 template<class T>
456 inline bool operator!=(T a, rational<T> const& b)
457 {
458 return not (a == b);
459 }
460
462 template<class T>
463 inline bool operator<=(rational<T> const& a, rational<T> const& b)
464 {
465 return not (b < a);
466 }
467
469 template<class T>
470 inline bool operator<=(rational<T> const& a, T const& b)
471 {
472 return not (b < a);
473 }
474
476 template<class T>
477 inline bool operator<=(T const& a, rational<T> const& b)
478 {
479 return not (b < a);
480 }
481
483 template<class T>
484 inline bool operator>(rational<T> const& a, rational<T> const& b)
485 {
486 return b < a;
487 }
488
490 template<class T>
491 inline bool operator>(rational<T> const& a, T const& b)
492 {
493 return b < a;
494 }
495
497 template<class T>
498 inline bool operator>(T const& a, rational<T> const& b)
499 {
500 return b < a;
501 }
502
504 template<class T>
505 inline bool operator>=(rational<T> const& a, rational<T> const& b)
506 {
507 return not (b > a);
508 }
509
511 template<class T>
512 inline bool operator>=(rational<T> const& a, T const& b)
513 {
514 return not (b > a);
515 }
516
518 template<class T>
519 inline bool operator>=(T const& a, rational<T> const& b)
520 {
521 return not (b > a);
522 }
523
525 template<class T, class Char, class Traits>
526 std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& in, rational<T>& rat)
527 {
528 typename std::basic_istream<Char, Traits>::sentry sentry{in, false};
529 ioflags flags{in};
530
531 T n{};
532 if (not (in >> n))
533 // Error reading the numerator.
534 return in;
535
536 in >> std::noskipws;
537 char sep{};
538 if (not (in >> sep))
539 // Error reading the separator character.
540 return in;
541 else if (sep != '/')
542 {
543 // Push sep back into the input stream, so the next input operation
544 // will read it.
545 in.unget();
546 rat = n;
547 return in;
548 }
549 else
550 {
551 T d{};
552 if (in >> d)
553 // Successfully read numerator, separator, and denominator.
554 rat = rational<T>{n, d};
555 }
556
557 return in;
558 }
559
561 template<class T, class Char, class Traits>
562 std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& out, rational<T> const& rat)
563 {
564 typename std::basic_ostream<Char, Traits>::sentry sentry{out};
565 std::ostringstream stream;
566 stream << rat.numerator() << '/' << rat.denominator();
567 out << stream.str();
568 return out;
569 }
570
571 #endif
operator!=
bool operator!=(number a, number b)
Definition: number.hpp:149
rational::operator=
rational & operator=(value_type)
Assignment of an integer.
Definition: rational.hpp:165
operator==
bool operator==(number a, number b)
Definition: number.hpp:138
rational::denominator
value_type denominator() const
Return the denominator.
Definition: rational.hpp:52
operator/
rational< T > operator/(rational< T > lhs, rational< T > const &rhs)
Division.
Definition: rational.hpp:373
rational::operator-=
rational & operator-=(rational const &rhs)
Subtraction assignment operator.
Definition: rational.hpp:200
rational::numerator
value_type numerator() const
Return the numerator.
Definition: rational.hpp:50
rational::operator--
rational & operator--()
Pre-decrement.
Definition: rational.hpp:270
absval
rational< T > absval(rational< T > const &r)
Definition: rational.hpp:292
rational::denominator_
value_type denominator_
Definition: rational.hpp:109
ioflags.hpp
Save and restore I/O stream flags.
rational::scale
value_type scale(U value)
operator-
number operator-(number n)
Definition: number.hpp:202
gcd.hpp
rational::operator+=
rational & operator+=(rational const &rhs)
Addition assignment operator.
Definition: rational.hpp:183
zero_denominator::zero_denominator
zero_denominator(std::string const &what)
Construct the exception object.
Definition: rational.hpp:21
rational::rational
rational(value_type num=0)
Definition: rational.hpp:36
gcd
T gcd(T n, T m)
Definition: gcd.hpp:8
rational::operator*=
rational & operator*=(rational const &rhs)
Multiplication assignment operator.
Definition: rational.hpp:217
rational::operator/=
rational & operator/=(rational const &rhs)
Division assignment operator.
Definition: rational.hpp:234
operator>>
std::basic_istream< Char, Traits > & operator>>(std::basic_istream< Char, Traits > &in, rational< T > &rat)
Input operator.
Definition: rational.hpp:526
rational
Represent a rational number (fraction) as a numerator and denominator.
Definition: rational.hpp:26
rational::normalize
void normalize()
Definition: rational.hpp:144
rational::numerator_
value_type numerator_
Definition: rational.hpp:108
operator+
rational< T > operator+(rational< T > lhs, rational< T > const &rhs)
Addition.
Definition: rational.hpp:300
power10.hpp
rational::value_type
T value_type
Convenience typedef for the integral type of the numerator and denominator.
Definition: rational.hpp:30
operator*
rational< T > operator*(rational< T > lhs, rational< T > const &rhs)
Multiplication.
Definition: rational.hpp:349
rational::operator++
rational & operator++()
Pre-increment.
Definition: rational.hpp:255
operator>
bool operator>(number a, number b)
Definition: number.hpp:182
zero_denominator
Exception class if the denominator is ever zero.
Definition: rational.hpp:17
ioflags
Definition: ioflags.hpp:15
rational::double_divisor
static constexpr value_type double_divisor
Definition: rational.hpp:106
operator>=
bool operator>=(number a, number b)
Definition: number.hpp:193
rational::reduce
void reduce()
Reduce the numerator and denominator by their GCD.
Definition: rational.hpp:155