NoisyArithmetic.cpp

Go to the documentation of this file.
00001 /*
00002 // $Id: //open/dev/fennel/calculator/NoisyArithmetic.cpp#3 $
00003 // Fennel is a library of data storage and processing components.
00004 // Copyright (C) 2005-2009 The Eigenbase Project
00005 // Copyright (C) 2004-2009 SQLstream, Inc.
00006 // Copyright (C) 2009-2009 LucidEra, Inc.
00007 //
00008 // This program is free software; you can redistribute it and/or modify it
00009 // under the terms of the GNU General Public License as published by the Free
00010 // Software Foundation; either version 2 of the License, or (at your option)
00011 // any later version approved by The Eigenbase Project.
00012 //
00013 // This program is distributed in the hope that it will be useful,
00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 // GNU General Public License for more details.
00017 //
00018 // You should have received a copy of the GNU General Public License
00019 // along with this program; if not, write to the Free Software
00020 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021 //
00022 // ---
00023 //
00024 // Arithmetic function on various types but Raises exceptions on various
00025 // error conditions (overflow, divide-by-zero etc.)
00026 //
00027 // TODO:
00028 //      1. How does little/big endian effect the bit shift used in
00029 //          unsigned multiplication?
00030 //      3. Shift/Bit/Round operator etc.
00031 //
00032 */
00033 /* TEMP. TODO. LATER, gcc>=4.1 is braindead: optimizes fp instructions until
00034     AFTER the fp status check (fetestexcept()). At the same time FENV_ACCESS ON
00035     is not yet implemented. ref. dtbug #1490.
00036 
00037    Ref. this macro in code and cleanup by removing redundant code
00038 */
00039 #define DTBUG1490   (1)
00040 
00041 #include "fennel/common/CommonPreamble.h"
00042 
00043 #include <assert.h>
00044 #ifndef DTBUG1490
00045 #pragma STDC FENV_ACCESS ON   /* notify compiler we expect to use fp
00046                                  exceptions, this is the standard way of
00047                                  stopping compiler from introducing
00048                                  optimizations which don't play well with fp
00049                                  status registers */
00050 #endif
00051 #ifndef __MSVC__
00052 #include <fenv.h>
00053 #endif
00054 #include <string>
00055 
00056 #include "NoisyArithmetic.h"
00057 
00058 FENNEL_BEGIN_CPPFILE("$Id: //open/dev/fennel/calculator/NoisyArithmetic.cpp#3 $");
00059 
00060 // TODO jvs 1-Apr-2009:  try noisy on Win32 again now that we have
00061 // upgraded the old STLport version we were using there
00062 #ifdef __MSVC__
00063 #define NOISY_DISABLED    (1)
00064 #endif
00065 
00066 /* TODO --- check these codes: 220 DATA_EXCEPTION */
00067 #define S_OVER    "22003"        /* NUMERIC_VALUE_OUT_OF_RANGE */
00068 #define S_UNDR    "22000"        /* ?? underflow */
00069 #define S_INVL    "22023"        /* INVALID_PARAMETER_VALUE */
00070 #define S_INEX    "22000"        /* ?? inexact */
00071 #define S_DIV0    "22012"        /* DIVISION_BY_ZERO */
00072 
00073 /* --- */
00074 static void Raise(
00075     TExceptionCBData *pData,
00076     TProgramCounter pc,
00077     const char *pMsg)
00078     throw(CalcMessage)
00079 {
00080     if (pData) {
00081         assert(pData->fnCB);
00082         (pData->fnCB)(pMsg, pData->pData);
00083     }
00084     throw CalcMessage(pMsg, pc);
00085 }
00086 
00087 #if defined(NOISY_DISABLED) && NOISY_DISABLED
00088 /*
00089 ** Disabled all tests (to be compatible with current implementation
00090 ** in order to allow current tests to succeed
00091 */
00092 #define DO(type)                                                        \
00093     template <> type Noisy<type>::add(                                  \
00094         TProgramCounter,                                                \
00095         const type left, const type right,                              \
00096         TExceptionCBData *pExData) throw(CalcMessage)                   \
00097     {                                                                   \
00098         return left + right;                                            \
00099     }                                                                   \
00100     template <> type Noisy<type>::sub(                                  \
00101         TProgramCounter,                                                \
00102         const type left, const type right,                              \
00103         TExceptionCBData *pExData) throw(CalcMessage)                   \
00104     {                                                                   \
00105         return left - right;                                            \
00106     }                                                                   \
00107     template <> type Noisy<type>::mul(                                  \
00108         TProgramCounter,                                                \
00109         const type left, const type right,                              \
00110         TExceptionCBData *pExData) throw(CalcMessage)                   \
00111     {                                                                   \
00112         return left * right;                                            \
00113     }                                                                   \
00114     template <> type Noisy<type>::div(                                  \
00115         TProgramCounter pc,                                             \
00116         const type left, const type right,                              \
00117         TExceptionCBData *pExData) throw(CalcMessage) {                 \
00118         if (right == 0) {                                               \
00119             Raise(pExData, pc, S_DIV0);                                 \
00120         }                                                               \
00121         return left / right;                                            \
00122     }                                                                   \
00123     template <> type Noisy<type>::neg(                                  \
00124         TProgramCounter,                                                \
00125         const type right,                                               \
00126         TExceptionCBData *pExData) throw(CalcMessage)                   \
00127     {                                                                   \
00128         return right * -1;                                              \
00129     }
00130 DO(char)
00131 DO(signed char)
00132 DO(unsigned char)
00133 DO(short)
00134 DO(unsigned short)
00135 DO(int)
00136 DO(unsigned int)
00137 #if __WORDSIZE == 64
00138 DO(long int)
00139 DO(long unsigned int)
00140 #else
00141 DO(long long int)
00142 DO(long long unsigned int)
00143 #endif
00144 DO(float)
00145 DO(double)
00146 DO(long double)
00147 
00148 #else
00149 #define UNSIGNED_ADD(type)                                              \
00150     template <> type Noisy<type>::add(                                  \
00151         TProgramCounter pc, const type left,                            \
00152         const type right, TExceptionCBData *pExData) throw(CalcMessage) \
00153     {                                                                   \
00154         register type result;                                           \
00155         if (left > (std::numeric_limits<type>::max() - right)) {        \
00156             Raise(pExData, pc, S_OVER);                                 \
00157         }                                                               \
00158         result = left + right;                                          \
00159         assert(result == (left + right));                               \
00160         return result;                                                  \
00161     }
00162 
00163 #define SIGNED_ADD(type)                                                \
00164     template <> type Noisy<type>::add(                                  \
00165         TProgramCounter pc, const type left,                            \
00166         const type right, TExceptionCBData *pExData) throw(CalcMessage) \
00167     {                                                                   \
00168         register type result = left + right;                            \
00169         if (left < 0 && right < 0 && result >=0) {                      \
00170             Raise(pExData, pc, S_OVER);                                 \
00171         }                                                               \
00172         if (left > 0 && right > 0 && result <= 0) {                     \
00173             Raise(pExData, pc, S_OVER);                                 \
00174         }                                                               \
00175         assert(result == (left + right));                               \
00176         return result;                                                  \
00177     }
00178 
00179 #define UNSIGNED_SUB(type)                                              \
00180     template <> type Noisy<type>::sub(                                  \
00181         TProgramCounter pc, const type left,                            \
00182         const type right, TExceptionCBData *pExData) throw(CalcMessage) \
00183     {                                                                   \
00184         register type result;                                           \
00185         if (right > left) {                                             \
00186             Raise(pExData, pc, S_OVER);                                 \
00187         }                                                               \
00188         return left - right;                                            \
00189         result = left - right;                                          \
00190         assert(result == (left - right));                               \
00191         return result;                                                  \
00192     }
00193 
00194 #define SIGNED_SUB(type)                                                \
00195     template <> type Noisy<type>::sub(                                  \
00196         TProgramCounter pc, const type left,                            \
00197         const type right, TExceptionCBData *pExData) throw(CalcMessage) \
00198     {                                                                   \
00199         register type r = right;                                        \
00200         register type l = left;                                         \
00201         register type result;                                           \
00202         if (r == std::numeric_limits<type>::min()) {                    \
00203             if (l == std::numeric_limits<type>::max()) {                \
00204                 Raise(pExData, pc, S_OVER);                             \
00205             }                                                           \
00206             r++;                                                        \
00207             l++;                                                        \
00208         }                                                               \
00209         result = Noisy<type>::add(pc, l, -r, pExData);                  \
00210         assert(result == (left - right));                               \
00211         return result;                                                  \
00212     }
00213 
00214 #define UNSIGNED_MUL(type)                                                  \
00215     template <> type Noisy<type>::mul(                                      \
00216         TProgramCounter pc, const type left,                                \
00217         const type right, TExceptionCBData *pExData) throw(CalcMessage)     \
00218     {                                                                   \
00219         register type result;                                           \
00220         if (left == 0 || right == 0) {                                  \
00221             return 0;                                                   \
00222         }                                                               \
00223         if (left > right) {                                             \
00224             return Noisy<type>::mul(pc, right, left, pExData);          \
00225         }                                                               \
00226         register type r = right;                                        \
00227         register type l = left;                                         \
00228         assert(l <= r);                                                 \
00229         const type msb = ~(std::numeric_limits<type>::max() >> 1);      \
00230         result = 0;                                                     \
00231         while (1) {                                                     \
00232             if (l & 0x1) {                                              \
00233                 result = Noisy<type>::add(pc, result, r, pExData);      \
00234             }                                                           \
00235             l >>= 1;                                                    \
00236             if (!l) {                                                   \
00237                 break;                                                  \
00238             }                                                           \
00239             if (msb & r) {                                              \
00240                 Raise(pExData, pc, S_OVER);                             \
00241             }                                                           \
00242             r <<= 1;                                                    \
00243         }                                                               \
00244         assert(result == (left * right));                               \
00245         return result;                                                  \
00246     }
00247 
00248 #define SIGNED_MUL(type)                                                \
00249     template <> type Noisy<type>::mul(                                  \
00250         TProgramCounter pc, const type left,                            \
00251         const type right, TExceptionCBData *pExData) throw(CalcMessage) \
00252     {                                                                   \
00253         register type result;                                           \
00254         if (left == 0 || right == 0) {                                  \
00255             return 0;                                                   \
00256         }                                                               \
00257         if (right > left) {                                             \
00258             return Noisy<type>::mul(pc, right, left, pExData);          \
00259         }                                                               \
00260         register type r = right;                                        \
00261         register type l = left;                                         \
00262         assert(r <= l);                                                 \
00263         if (l < 0 /* infers r<0, both negative */) {                    \
00264             if (r == std::numeric_limits<type>::min()) {                \
00265                 Raise(pExData, pc, S_OVER);                             \
00266             }                                                           \
00267             assert(l != std::numeric_limits<type>::min());              \
00268             l = -l;                                                     \
00269             r = -r;                                                     \
00270         }                                                               \
00271         assert(l > 0);                                                  \
00272         const type n_max = std::numeric_limits<type>::min() >> 1;       \
00273         const type p_max = (-n_max) - 1;                                \
00274         result = 0;                                                     \
00275         while (1) {                                                     \
00276             if (l & 0x1) {                                              \
00277                 result=Noisy<type>::add(pc, result, r, pExData);        \
00278             }                                                           \
00279             l >>= 1;                                                    \
00280             if (!l) {                                                   \
00281                 break;                                                  \
00282             }                                                           \
00283             if (r < n_max || r > p_max) {                               \
00284                 Raise(pExData, pc, S_OVER);                             \
00285             }                                                           \
00286             r *= 2;                                                     \
00287         }                                                               \
00288         assert(result == (left * right));                               \
00289         return result;                                                  \
00290     }
00291 
00292 #define UNSIGNED_DIV(type)                                              \
00293     template <> type Noisy<type>::div(                                  \
00294         TProgramCounter pc, const type left,                            \
00295         const type right, TExceptionCBData *pExData) throw(CalcMessage) \
00296     {                                                                   \
00297         if (right == 0) {                                               \
00298             Raise(pExData, pc, S_DIV0);                                 \
00299         }                                                               \
00300         register type result = left / right;                            \
00301         assert(result == (left / right));                               \
00302         return result;                                                  \
00303     }
00304 
00305 #define SIGNED_DIV(type)                                                \
00306     template <> type Noisy<type>::div(                                  \
00307         TProgramCounter pc, const type left,                            \
00308         const type right, TExceptionCBData *pExData) throw(CalcMessage) \
00309     {                                                                   \
00310         /* this only holds for 2's complement representations */        \
00311         if (left == std::numeric_limits<type>::min() && right == -1) {  \
00312             Raise(pExData, pc, S_OVER);                                 \
00313         }                                                               \
00314         if (right == 0) {                                               \
00315             Raise(pExData, pc, S_DIV0);                                 \
00316         }                                                               \
00317         register type result = left / right;                            \
00318         assert(result == (left / right));                               \
00319         return result;                                                  \
00320     }
00321 
00322 #define UNSIGNED_NEG(type)                                              \
00323     template <> type Noisy<type>::neg(                                  \
00324         TProgramCounter pc,                                             \
00325         const type right, TExceptionCBData *pExData) throw(CalcMessage) \
00326     {                                                                   \
00327         if (right != 0) {                                               \
00328             Raise(pExData, pc, S_OVER);                                 \
00329         }                                                               \
00330         return 0;                                                       \
00331     }
00332 
00333 #define SIGNED_NEG(type)                                                \
00334     template <> type Noisy<type>::neg(                                  \
00335         TProgramCounter pc,                                             \
00336         const type right, TExceptionCBData *pExData) throw(CalcMessage) \
00337     {                                                                   \
00338         /* this only holds for 2's complement representations */        \
00339         if (right == std::numeric_limits<type>::min()) {                \
00340             Raise(pExData, pc, S_OVER);                                 \
00341         }                                                               \
00342         return -(right);                                                \
00343     }
00344 
00345 /* --- */
00346 inline void maybe_raise_fe_exception(
00347     TExceptionCBData *pExData,
00348     TProgramCounter pc)
00349     throw(CalcMessage)
00350 {
00351     int fe = ::fetestexcept(FE_ALL_EXCEPT);
00352     if (0) {
00353     } else if (fe & FE_DIVBYZERO) {
00354         Raise(pExData, pc, S_DIV0);
00355     } else if (fe & FE_UNDERFLOW) {
00356         Raise(pExData, pc, S_UNDR);
00357     } else if (fe & FE_OVERFLOW) {
00358         Raise(pExData, pc, S_OVER);
00359     } else if (fe & FE_INVALID) {
00360         Raise(pExData, pc, S_INVL);
00361 
00362     /* leave this last because it occurs in conjunction with other
00363         flags */
00364     } else if (fe & FE_INEXACT) {
00365 /* disabling inexact. for <float> 200.0 + 0.3 == 200.300003 and S_INEX
00366  gets set., so is too pedestrian a case to raise an error for
00367         Raise(pExData, pc, S_INEX); */
00368         }
00369 }
00370 
00371 /* ---
00372 Removing the following dev. time sanity checks here b/c they may fail
00373 because of an ignored S_INEX, see above comment.
00374     assert(result == (left+right));                                       \
00375     assert(result == (left-right));                                       \
00376     assert(result == (left*right));                                       \
00377     assert(result == (left/right));                                       \
00378     assert(result == (-right));                                           \
00379 --- */
00380 
00381 
00382 
00383 #if defined(DTBUG1490) && DTBUG1490
00384     /* instruct gcc *NEVER* to inline functions, thereby forcing the actual fp
00385     operation to occur before we test it's result.... */
00386 #   define OP_FN_PROLOG static void __attribute__((noinline))
00387 #else
00388     /* optimize to inplace operations, even at -O1 */
00389 #   define inline void
00390 #endif
00391 template <typename TYPE> OP_FN_PROLOG na_add(
00392     TYPE &res, const TYPE &r, const TYPE &l)
00393 {
00394     res = r + l;
00395 }
00396 
00397 template <typename TYPE> OP_FN_PROLOG na_sub(
00398     TYPE &res, const TYPE &r, const TYPE &l)
00399 {
00400     res = r - l;
00401 }
00402 
00403 template <typename TYPE> OP_FN_PROLOG na_mul(
00404     TYPE &res, const TYPE &r, const TYPE &l)
00405 {
00406     res = r * l;
00407 }
00408 
00409 template <typename TYPE> OP_FN_PROLOG na_div(
00410     TYPE &res, const TYPE &r, const TYPE &l)
00411 {
00412     res = r / l;
00413 }
00414 
00415 template <typename TYPE> OP_FN_PROLOG na_neg(TYPE &res, const TYPE &r)
00416 {
00417     res = -r;
00418 }
00419 
00420 #define FLOATING_ADD(type)                                              \
00421     template <> type Noisy<type>::add(                                  \
00422         TProgramCounter pc, const type left,                            \
00423         const type right, TExceptionCBData *pExData) throw(CalcMessage) \
00424     {                                                                   \
00425         type result;                                                    \
00426         ::feclearexcept(FE_ALL_EXCEPT);                                 \
00427         na_add<type>(result, left, right);                              \
00428         maybe_raise_fe_exception(pExData, pc);                          \
00429         return result;                                                  \
00430     }
00431 
00432 #define FLOATING_SUB(type)                                              \
00433     template <> type Noisy<type>::sub(                                  \
00434         TProgramCounter pc, const type left,                            \
00435         const type right, TExceptionCBData *pExData) throw(CalcMessage) \
00436     {                                                                       \
00437         type result;                                                    \
00438         ::feclearexcept(FE_ALL_EXCEPT);                                 \
00439         na_sub<type>(result, left, right);                              \
00440         maybe_raise_fe_exception(pExData, pc);                          \
00441         return result;                                                  \
00442     }
00443 
00444 #define FLOATING_MUL(type)                                              \
00445     template <> type Noisy<type>::mul(                                  \
00446         TProgramCounter pc, const type left,                            \
00447         const type right, TExceptionCBData *pExData) throw(CalcMessage) \
00448     {                                                                   \
00449         type result;                                                    \
00450         ::feclearexcept(FE_ALL_EXCEPT);                                 \
00451         na_mul<type>(result, left, right);                              \
00452         maybe_raise_fe_exception(pExData, pc);                          \
00453         return result;                                                  \
00454     }
00455 
00456 #define FLOATING_DIV(type)                                              \
00457     template <> type Noisy<type>::div(                                  \
00458         TProgramCounter pc, const type left,                            \
00459         const type right, TExceptionCBData *pExData) throw(CalcMessage) \
00460     {                                                                   \
00461         type result;                                                    \
00462         ::feclearexcept(FE_ALL_EXCEPT);                                 \
00463         na_div<type>(result, left, right);                              \
00464         maybe_raise_fe_exception(pExData, pc);                          \
00465         return result;                                                  \
00466     }
00467 
00468 #define FLOATING_NEG(type)                                              \
00469     template <> type Noisy<type>::neg(                                  \
00470         TProgramCounter pc,                                             \
00471         const type right, TExceptionCBData *pExData) throw(CalcMessage) \
00472     {                                                                   \
00473         type result;                                                    \
00474         ::feclearexcept(FE_ALL_EXCEPT);                                 \
00475         result = (-right);                                              \
00476         na_neg<type>(result, right);                                    \
00477         maybe_raise_fe_exception(pExData, pc);                          \
00478         return result;                                                  \
00479     }
00480 
00481 SIGNED_ADD(char)
00482 SIGNED_ADD(signed char)
00483 SIGNED_ADD(short)
00484 SIGNED_ADD(int)
00485 SIGNED_ADD(long long int)
00486 
00487 UNSIGNED_ADD(unsigned char)
00488 UNSIGNED_ADD(unsigned short)
00489 UNSIGNED_ADD(unsigned int)
00490 UNSIGNED_ADD(unsigned long long int)
00491 
00492 SIGNED_SUB(char)
00493 SIGNED_SUB(signed char)
00494 SIGNED_SUB(short)
00495 SIGNED_SUB(int)
00496 SIGNED_SUB(long long int)
00497 
00498 UNSIGNED_SUB(unsigned char)
00499 UNSIGNED_SUB(unsigned short)
00500 UNSIGNED_SUB(unsigned int)
00501 UNSIGNED_SUB(unsigned long long int)
00502 
00503 SIGNED_MUL(char)
00504 SIGNED_MUL(signed char)
00505 SIGNED_MUL(short)
00506 SIGNED_MUL(int)
00507 SIGNED_MUL(long long int)
00508 
00509 UNSIGNED_MUL(unsigned char)
00510 UNSIGNED_MUL(unsigned short)
00511 UNSIGNED_MUL(unsigned int)
00512 UNSIGNED_MUL(unsigned long long int)
00513 
00514 SIGNED_DIV(char)
00515 SIGNED_DIV(signed char)
00516 SIGNED_DIV(short)
00517 SIGNED_DIV(int)
00518 SIGNED_DIV(long long int)
00519 
00520 SIGNED_NEG(char)
00521 SIGNED_NEG(signed char)
00522 SIGNED_NEG(short)
00523 SIGNED_NEG(int)
00524 SIGNED_NEG(long long int)
00525 
00526 UNSIGNED_DIV(unsigned char)
00527 UNSIGNED_DIV(unsigned short)
00528 UNSIGNED_DIV(unsigned int)
00529 UNSIGNED_DIV(unsigned long long int)
00530 
00531 UNSIGNED_NEG(unsigned char)
00532 UNSIGNED_NEG(unsigned short)
00533 UNSIGNED_NEG(unsigned int)
00534 UNSIGNED_NEG(unsigned long long int)
00535 
00536 #if __WORDSIZE == 64
00537 SIGNED_ADD(long int)
00538 UNSIGNED_ADD(unsigned long int)
00539 SIGNED_SUB(long int)
00540 UNSIGNED_SUB(unsigned long int)
00541 SIGNED_MUL(long int)
00542 UNSIGNED_MUL(unsigned long int)
00543 SIGNED_DIV(long int)
00544 SIGNED_NEG(long int)
00545 UNSIGNED_DIV(unsigned long int)
00546 UNSIGNED_NEG(unsigned long int)
00547 #endif
00548 
00549 FLOATING_ADD(float)
00550 FLOATING_ADD(double)
00551 FLOATING_ADD(long double)
00552 
00553 FLOATING_SUB(float)
00554 FLOATING_SUB(double)
00555 FLOATING_SUB(long double)
00556 
00557 FLOATING_MUL(float)
00558 FLOATING_MUL(double)
00559 FLOATING_MUL(long double)
00560 
00561 FLOATING_DIV(float)
00562 FLOATING_DIV(double)
00563 FLOATING_DIV(long double)
00564 
00565 FLOATING_NEG(float)
00566 FLOATING_NEG(double)
00567 FLOATING_NEG(long double)
00568 
00569 #undef UNSIGNED_ADD
00570 #undef SIGNED_ADD
00571 #undef FLOATING_ADD
00572 #undef UNSIGNED_SUB
00573 #undef SIGNED_SUB
00574 #undef FLOATING_SUB
00575 #undef UNSIGNED_MUL
00576 #undef SIGNED_MUL
00577 #undef FLOATING_MUL
00578 #undef UNSIGNED_DIV
00579 #undef SIGNED_DIV
00580 #undef FLOATING_DIV
00581 #undef UNSIGNED_NEG
00582 #undef SIGNED_NEG
00583 #undef FLOATING_NEG
00584 
00585 #endif /* disabled or not */
00586 
00587 FENNEL_END_CPPFILE("$Id: //open/dev/fennel/calculator/NoisyArithmetic.cpp#3 $");
00588 
00589 // End NoisyArithmetic.cpp

Generated on Mon Jun 22 04:00:17 2009 for Fennel by  doxygen 1.5.1