• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE section PUBLIC "-//Boost//DTD BoostBook XML V1.1//EN"
3"http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
4<section id="safe_numerics.eliminate_runtime_penalty">
5  <title>Eliminating Runtime Penalty</title>
6
7  <para>Up until now, we've mostly focused on detecting when incorrect results
8  are produced and handling these occurrences either by throwing an exception
9  or invoking some designated function. We've achieved our goal of detecting
10  and handling arithmetically incorrect behavior - but at cost of checking
11  many arithmetic operations at runtime. It is a fact that many C++
12  programmers will find this trade-off unacceptable. So the question arises as
13  to how we might minimize or eliminate this runtime penalty.</para>
14
15  <para>The first step is to determine what parts of a program might invoke
16  exceptions. The following program is similar to previous examples but uses a
17  special exception policy: <link
18  linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>.</para>
19
20  <para><programlisting><xi:include href="../../example/example81.cpp"
21        parse="text" xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>Now,
22  any expression which <emphasis><emphasis
23  role="bold">might</emphasis></emphasis> fail at runtime is flagged with a
24  compile time error. There is no longer any need for <code>try/catch</code>
25  blocks. Since this program does not compile, the <emphasis
26  role="bold">library absolutely <emphasis role="bold">guarantees that no
27  arithmetic expression</emphasis> will yield incorrect results</emphasis>.
28  Furthermore, it is <emphasis role="bold">absolutely guaranteed that no
29  exception will ever be thrown</emphasis>. This is our original goal.</para>
30
31  <para>Now all we need to do is make the program compile. There are a couple
32  of ways to achieve this.</para>
33
34  <section id="safe_numerics.eliminate_runtime_penalty.2">
35    <title>Using <link linkend="safe_numerics.safe_range">safe_range</link>
36    and <link linkend="safe_numerics.safe_literal">safe_literal</link></title>
37
38    <para>When trying to avoid arithmetic errors of the above type,
39    programmers will select data types which are wide enough to hold values
40    large enough to be certain that results won't overflow, but are not so
41    large as to make the program needlessly inefficient. In the example below,
42    we presume we know that the values we want to work with fall in the range
43    [-24,82]. So we "know" the program will always result in a correct result.
44    But since we trust no one, and since the program could change and the
45    expressions be replaced with other ones, we'll still use the <link
46    linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
47    exception policy to verify at compile time that what we "know" to be true
48    is in fact true.</para>
49
50    <programlisting><xi:include href="../../example/example83.cpp"
51        parse="text" xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>
52
53    <para><itemizedlist>
54        <listitem>
55          <para><code><code>safe_signed_range</code></code> defines a type
56          which is limited to the indicated range. Out of range assignments
57          will be detected at compile time if possible (as in this case) or at
58          run time if necessary.</para>
59        </listitem>
60
61        <listitem>
62          <para>A safe range could be defined with the same minimum and
63          maximum value effectively restricting the type to holding one
64          specific value. This is what <code>safe_signed_literal</code>
65          does.</para>
66        </listitem>
67
68        <listitem>
69          <para>Defining constants with <code>safe_signed_literal</code>
70          enables the library to correctly anticipate the correct range of the
71          results of arithmetic expressions at compile time.</para>
72        </listitem>
73
74        <listitem>
75          <para>The usage of <code><link
76          linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link></code>
77          will mean that any assignment to z which could be outside its legal
78          range will result in a compile time error.</para>
79        </listitem>
80
81        <listitem>
82          <para>All safe integer operations are implemented as constant
83          expressions. The usage of <code>constexpr</code> will guarantee that
84          <code>z</code> will be available at compile time for any subsequent
85          use.</para>
86        </listitem>
87
88        <listitem>
89          <para>So if this program compiles, it's guaranteed to return a valid
90          result.</para>
91        </listitem>
92      </itemizedlist>The output uses a custom output manipulator,
93    <code>safe_format</code>, for safe types to display the underlying type
94    and its range as well as current value. This program produces the
95    following run time output.</para>
96
97    <screen>example 83:
98x = &lt;signed char&gt;[10,10] = 10
99y = &lt;signed char&gt;[67,67] = 67
100x + y = &lt;int&gt;[77,77] = 77
101z = &lt;signed char&gt;[-24,82] = 77</screen>
102
103    <para>Take note of the various variable types:<itemizedlist>
104        <listitem>
105          <para><code>x</code> and <code>y</code> are safe types with fixed
106          ranges which encompass one single value. They can hold only that
107          value which they have been assigned at compile time.</para>
108        </listitem>
109
110        <listitem>
111          <para><code>The sum x + y can also be determined at compile
112          time.</code></para>
113        </listitem>
114
115        <listitem>
116          <para>The type of z is defined so that It can hold only values in
117          the closed range -24,82. We can assign the sum of x + y because it
118          is in the range that <code>z</code> is guaranteed to hold. If the
119          sum could not be be guaranteed to fall in the range of
120          <code>z</code>, we would get a compile time error due to the fact we
121          are using the <code>loose_trap_policy</code> exception
122          policy.</para>
123        </listitem>
124      </itemizedlist>All this information regarding the range and values of
125    variables has been determined at compile time. There is no runtime
126    overhead. The usage of safe types does not alter the calculations or
127    results in anyway. So <code>safe_t</code> and <code>const_safe_t</code>
128    could be redefined to <code>int</code> and <code>const int</code>
129    respectively and the program would operate identically - although it might
130    We could compile the program for another machine - as is common when
131    building embedded systems and know (assuming the target machine
132    architecture was the same as our native one) that no erroneous results
133    would ever be produced.</para>
134  </section>
135
136  <section id="safe_numerics.eliminate_runtime_penalty.1">
137    <title>Using Automatic Type Promotion</title>
138
139    <para>The C++ standard describes how binary operations on different
140    integer types are handled. Here is a simplified version of the
141    rules:</para>
142
143    <itemizedlist>
144      <listitem>
145        <para>promote any operand smaller than <code>int</code> to an
146        <code>int</code> or <code>unsigned int</code>.</para>
147      </listitem>
148
149      <listitem>
150        <para>if the size of the signed operand is larger than the size of the
151        signed operand, the type of the result will be signed. Otherwise, the
152        type of the result will be unsigned.</para>
153      </listitem>
154
155      <listitem>
156        <para>Convert the type each operand to the type of the result,
157        expanding the size as necessary.</para>
158      </listitem>
159
160      <listitem>
161        <para>Perform the operation the two resultant operands.</para>
162      </listitem>
163    </itemizedlist>
164
165    <para>So the type of the result of some binary operation may be different
166    than the types of either or both of the original operands.</para>
167
168    <para>If the values are large, the result can exceed the size that the
169    resulting integer type can hold. This is what we call "overflow". The
170    C/C++ standard characterizes this as undefined behavior and leaves to
171    compiler implementors the decision as to how such a situation will be
172    handled. Usually, this means just truncating the result to fit into the
173    result type - which sometimes will make the result arithmetically
174    incorrect. However, depending on the compiler and compile time switch
175    settings, such cases may result in some sort of run time exception or
176    silently producing some arbitrary result.</para>
177
178    <para>The complete signature for a safe integer type is:</para>
179
180    <para><programlisting>template &lt;
181    class T,                  // underlying integer type
182    class P = native,         // type promotion policy class
183    class E = default_exception_policy // error handling policy class
184&gt;
185safe;
186</programlisting></para>
187
188    <para>The promotion rules for arithmetic operations are implemented in the
189    default <code><link
190    linkend="safe_numerics.promotion_policies.native">native</link></code>
191    type promotion policy are consistent with those of standard C++</para>
192
193    <para>Up until now, we've focused on detecting when an arithmetic error
194    occurs and invoking an exception or other kind of error handler.</para>
195
196    <para>But now we look at another option. Using the <link
197    linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
198    type promotion policy, we can change the rules of C++ arithmetic for safe
199    types to something like the following:</para>
200
201    <para><itemizedlist>
202        <listitem>
203          <para>for any C++ numeric type, we know from <ulink
204          url="http://en.cppreference.com/w/cpp/types/numeric_limits"><code>std::numeric_limits</code></ulink>
205          what the maximum and minimum values that a variable can be - this
206          defines a closed interval.</para>
207        </listitem>
208
209        <listitem>
210          <para>For any binary operation on these types, we can calculate the
211          interval of the result at compile time.</para>
212        </listitem>
213
214        <listitem>
215          <para>From this interval we can select a new type which can be
216          guaranteed to hold the result and use this for the calculation. This
217          is more or less equivalent to the following code:</para>
218
219          <programlisting>int x, y;
220int z = x + y               // could overflow
221// so replace with the following:
222int x, y;
223long z = (long)x + (long)y; // can never overflow</programlisting>
224
225          <para>One could do this by editing his code manually as above, but
226          such a task would be tedious, error prone, non-portable and leave
227          the resulting code hard to read and verify. Using the <link
228          linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
229          type promotion policy will achieve the equivalent result without
230          these problems.</para>
231        </listitem>
232      </itemizedlist></para>
233
234    <para>When using the <link
235    linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
236    type promotion policy, with a given a binary operation, we silently
237    promote the types of the operands to a wider result type so the result
238    cannot overflow. This is a fundamental departure from the C++ Standard
239    behavior.</para>
240
241    <para>If the interval of the result cannot be guaranteed to fit in the
242    largest type that the machine can handle (usually 64 bits these days), the
243    largest available integer type with the correct result sign is used. So
244    even with our "automatic" type promotion scheme, it's still possible to
245    overflow. So while our <link
246    linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
247    type promotion policy might eliminate exceptions in our example above, it
248    wouldn't be guaranteed to eliminate them for all programs.</para>
249
250    <para>Using the <link
251    linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
252    exception policy will produce a compile time error anytime it's possible
253    for an error to occur.</para>
254
255    <para>This small example illustrates how to use automatic type promotion
256    to eliminate all runtime penalty.</para>
257
258    <para><programlisting><xi:include href="../../example/example82.cpp"
259          parse="text" xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting></para>
260
261    <itemizedlist>
262      <listitem>
263        <para>the <link
264        linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
265        type promotion policy has rendered the result of the sum of two
266        <code>integers</code> as a <code>safe&lt;long</code>&gt; type.</para>
267      </listitem>
268
269      <listitem>
270        <para>our program compiles without error - even when using the <link
271        linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
272        exception policy. This is because since a <code>long</code> can always
273        hold the result of the sum of two integers.</para>
274      </listitem>
275
276      <listitem>
277        <para>We do not need to use the <code>try/catch</code> idiom to handle
278        arithmetic errors - we will have no exceptions.</para>
279      </listitem>
280
281      <listitem>
282        <para>We only needed to change two lines of code to achieve our goal
283        of guaranteed program correctness with no runtime penalty.</para>
284      </listitem>
285    </itemizedlist>
286
287    <para>The above program produces the following output:</para>
288
289    <para><screen>example 82:
290x = &lt;int&gt;[-2147483648,2147483647] = 2147483647
291y = &lt;int&gt;[-2147483648,2147483647] = 2
292x + y = &lt;long&gt;[-4294967296,4294967294] = 2147483649
293</screen></para>
294
295    <para>Note that if any time in the future we were to change
296    safe&lt;int&gt; to safe&lt;long long&gt; the program could now overflow.
297    But since we're using <link
298    linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
299    the modified program would fail to compile. At this point we'd have to
300    alter our yet program again to eliminate run time penalty or set aside our
301    goal of zero run time overhead and change the exception policy to <link
302    linkend="safe_numerics.exception_policies.default_exception_policy"><code>default_exception_policy</code></link>
303    .</para>
304
305    <para>Note that once we use automatic type promotion, our programming
306    language isn't C/C++ anymore. So don't be tempted to so something like the
307    following:</para>
308
309    <programlisting>// DON'T DO THIS !
310#if defined(NDEBUG)
311using safe_t = boost::numeric::safe&lt;
312    int,
313    boost::numeric::automatic, // note use of "automatic" policy!!!
314    boost::numeric::loose_trap_policy
315&gt;;
316#else
317using safe_t = boost::numeric::safe&lt;int&gt;;
318#endif
319</programlisting>
320  </section>
321
322  <section id="safe_numerics.eliminate_runtime_penalty.3">
323    <title>Mixing Approaches</title>
324
325    <para>For purposes of exposition, we've divided the discussion of how to
326    eliminate runtime penalties by the different approaches available. A
327    realistic program could likely include all techniques mentioned above.
328    Consider the following:</para>
329
330    <programlisting><xi:include href="../../example/example84.cpp"
331        parse="text" xmlns:xi="http://www.w3.org/2001/XInclude"/></programlisting>
332
333    <para><itemizedlist>
334        <listitem>
335          <para>As before, we define a type <code>safe_t</code> to reflect our
336          view of legal values for this program. This uses the <link
337          linkend="safe_numerics.promotion_policies.automatic"><code>automatic</code></link>
338          type promotion policy as well as the <link
339          linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
340          exception policy to enforce elimination of runtime penalties.</para>
341        </listitem>
342
343        <listitem>
344          <para>The function <code>f</code> accepts only arguments of type
345          <code>safe_t</code> so there is no need to check the input values.
346          This performs the functionality of <emphasis><emphasis
347          role="bold">programming by contract</emphasis></emphasis> with no
348          runtime cost.</para>
349        </listitem>
350
351        <listitem>
352          <para>In addition, we define <code>input_safe_t</code> to be used
353          when reading variables from the program console. Clearly, these can
354          only be checked at runtime so they use the throw_exception policy.
355          When variables are read from the console they are checked for legal
356          values. We need no ad hoc code to do this, as these types are
357          guaranteed to contain legal values and will throw an exception when
358          this guarantee is violated. In other words, we automatically get
359          checking of input variables with no additional programming.</para>
360        </listitem>
361
362        <listitem>
363          <para>On calling of the function <code>f</code>, arguments of type
364          <code>input_safe_t</code> are converted to values of type
365          <code>safe_t</code> . In this particular example, it can be
366          determined at compile time that construction of an instance of a
367          <code>safe_t</code> from an <code>input_safe_t</code> can never
368          fail. Hence, no <code>try/catch</code> block is necessary. The usage
369          of the <link
370          linkend="safe_numerics.exception_policies.loose_trap_policy"><code>loose_trap_policy</code></link>
371          policy for <code>safe_t</code> types guarantees this to be true at
372          compile time.</para>
373        </listitem>
374      </itemizedlist>Here is the output from the program when values 12 and 32
375    are input from the console:</para>
376
377    <screen>example 84:
378type in values in format x y:33 45
379x&lt;signed char&gt;[-24,82] = 33
380y&lt;signed char&gt;[-24,82] = 45
381z = &lt;short&gt;[-48,164] = 78
382(x + y) = &lt;short&gt;[-48,164] = 78
383(x - y) = &lt;signed char&gt;[-106,106] = -12
384&lt;short&gt;[-48,164] = 78
385</screen>
386  </section>
387</section>
388