1.. _tutorial_commonops:
2
3Common operators
4================
5
6After having explained how mp++'s :ref:`type coercion <tutorial_numtower>` works, we can now move on to
7describe how mp++'s classes can be used to perform mathematical computations.
8
9All of mp++'s multiprecision classes support the four basic arithmetic operations (:math:`+`, :math:`-`,
10:math:`\times` and :math:`\div`) via overloaded binary operators:
11
12.. code-block:: c++
13
14   auto r1 = int_t{4} + int_t{3};
15   assert(r1 == 7);
16
17   auto r2 = rat_t{4, 3} - rat_t{2, 5};
18   assert(r2 == rat_t{14, 15});
19
20   auto r3 = real128{5} * real128{2};
21   assert(r3 == 10.);
22
23   auto r4 = real{5} / real{2};
24   assert(r4 == 2.5);
25
26It is of course possible to mix operands of different types, and the type of the result will be determined
27by the :ref:`type coercion rules <tutorial_numtower>` explained earlier:
28
29.. code-block:: c++
30
31   auto r1 = int_t{4} + 3;        // r1 will be of type int_t.
32   assert(r1 == 7);
33
34   auto r2 = -0.75 + rat_t{1, 2}; // r2 will be of type double.
35   assert(r2 == -.25);
36
37   auto r3 = real128{5} * 2;      // r3 will be of type real128.
38   assert(r3 == 10.);
39
40   auto r4 = real{5} / int_t{2};  // r4 will be of type real.
41   assert(r4 == 2.5);
42
43The behaviour of the division operator varies depending on the types involved. If only
44integral types are involved, division truncates, and division by zero throws
45a :cpp:class:`~mppp::zero_division_error` exception:
46
47.. code-block:: c++
48
49   auto r1 = int_t{5} / 2; // Integral division truncates.
50   assert(r1 == 2);
51
52   int_t{1} / 0;           // This will throw a zero_division_error exception.
53
54If floating-point types are involved, division by zero is allowed and results in infinity:
55
56.. code-block:: c++
57
58   auto r1 = real128{1} / 0; // Floating-point division by zero generates an infinity.
59   assert(isinf(r1))
60
61Rational division is always exact, unless the divisor is zero:
62
63.. code-block:: c++
64
65   auto r1 = rat_t{3} / 4;
66   assert(r1 == rat_t{3, 4});
67
68   rat_t{2} / 0;              // This will throw a zero_division_error exception.
69
70The in-place versions of the binary operators are available as well. Given a binary operator, its
71corresponding in-place counterpart behaves as a binary operation followed by assignment:
72
73.. code-block:: c++
74
75   int_t r1{4};
76   r1 += 5;                    // Equivalent to: r1 = r1 + 5
77   assert(r1 == 9);
78
79   rat_t r2{4, 3};
80   r2 -= 1.5;                  // Equivalent to: r2 = r2 - 1.5
81   assert(r2 == rat_t{-1, 6});
82
83   real128 r3{5};
84   r3 *= rat_t{1, 2};          // Equivalent to: r3 = r3 * rat_t{1, 2}
85   assert(r3 == 2.5);
86
87   real r4{42};
88   r4 /= real128{0};           // Equivalent to: r4 = r4 / real128{0}
89   assert(isinf(r4));
90
91It is also possible to use fundamental C++ types on the left-hand side of in-place operators:
92
93.. code-block:: c++
94
95   int n = 5;
96   n += int_t{5};
97   assert(n == 10);
98
99   n -= rat_t{3, 4}
100   assert(n == 9);
101
102   double x = 1.5;
103   x *= real128{2};
104   assert(x == 3.);
105
106   x /= real{3};
107   assert(x == 1.);
108
109The identity, negation, pre/post increment/decrement operators are also supported for all of
110mp++'s multiprecision classes:
111
112.. code-block:: c++
113
114   int_t n;
115   assert(++n == 1);
116   n++;
117   assert(n == 2);
118   assert(--n == 1);
119   n--;
120   assert(n == 0);
121
122   rat_t q{1, 2};
123   assert(+q == q);
124   assert(-q == rat_t{-1, 2});
125
126All of mp++'s multiprecision classes are contextually convertible to ``bool``, following the usual rule
127that nonzero values convert to ``true`` and zero values convert to ``false``:
128
129.. code-block:: c++
130
131   int_t n{3};
132   if (n) {
133      std::cout << "n is nonzero!\n";
134   }
135
136   rat_t q{0};
137   if (!q) {
138      std::cout << "q is zero!\n";
139   }
140
141   real r{1.23};
142   if (!!r) {
143      std::cout << "r is nonzero!\n";
144   }
145
146In addition to the common arithmetic operators, all of mp++'s multiprecision classes support the relational
147operators :math:`=` and :math:`\neq`. Real-valued types also support the comparison operators
148:math:`>`, :math:`\geq`, :math:`<` and :math:`\leq`. Any combination
149of multiprecision and numerical C++ types is supported:
150
151.. code-block:: c++
152
153   assert(int_t{42} == 42);
154   assert(3 != rat_t{1, 3});
155   assert(0.9 < int_t{1});
156   assert(real128{15} <= int_t{15});
157   assert(real{"inf", 100} > rat_t{123, 456});
158   assert(int_t{4} >= rat_t{16, 4});
159
160The comparison operators treat NaN values in the standard way: comparing NaN to any other value returns always
161``false``, apart from the :math:`\neq` operator which always returns ``true`` when NaN is involved. For the floating-point multiprecision
162classes, custom comparison functions with special NaN handling are also available (e.g., :cpp:func:`~mppp::real_lt()`,
163:cpp:func:`~mppp::real128_equal_to()`, etc.). These functions can be used as replacements for the comparison operators
164in facilities of the standard library such as ``std::sort()``, ``std::set``, etc.
165