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 = <signed char>[10,10] = 10 99y = <signed char>[67,67] = 67 100x + y = <int>[77,77] = 77 101z = <signed char>[-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 < 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> 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<long</code>> 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 = <int>[-2147483648,2147483647] = 2147483647 291y = <int>[-2147483648,2147483647] = 2 292x + y = <long>[-4294967296,4294967294] = 2147483649 293</screen></para> 294 295 <para>Note that if any time in the future we were to change 296 safe<int> to safe<long long> 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< 312 int, 313 boost::numeric::automatic, // note use of "automatic" policy!!! 314 boost::numeric::loose_trap_policy 315>; 316#else 317using safe_t = boost::numeric::safe<int>; 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<signed char>[-24,82] = 33 380y<signed char>[-24,82] = 45 381z = <short>[-48,164] = 78 382(x + y) = <short>[-48,164] = 78 383(x - y) = <signed char>[-106,106] = -12 384<short>[-48,164] = 78 385</screen> 386 </section> 387</section> 388