Drizzled Public API Documentation

round.cc

00001 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2008 Sun Microsystems, Inc.
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; version 2 of the License.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018  */
00019 
00020 #include <config.h>
00021 
00022 #include <math.h>
00023 #include <limits.h>
00024 
00025 #include <limits>
00026 #include <algorithm>
00027 
00028 #include <drizzled/function/math/round.h>
00029 #include <drizzled/util/test.h>
00030 
00031 namespace drizzled
00032 {
00033 
00034 extern const double log_10[309];
00035 
00036 
00037 using namespace std;
00038 
00039 void Item_func_round::fix_length_and_dec()
00040 {
00041   int      decimals_to_set;
00042   int64_t val1;
00043   bool     val1_unsigned;
00044 
00045   unsigned_flag= args[0]->unsigned_flag;
00046   if (!args[1]->const_item())
00047   {
00048     max_length= args[0]->max_length;
00049     decimals= args[0]->decimals;
00050     if (args[0]->result_type() == DECIMAL_RESULT)
00051     {
00052       max_length++;
00053       hybrid_type= DECIMAL_RESULT;
00054     }
00055     else
00056       hybrid_type= REAL_RESULT;
00057     return;
00058   }
00059 
00060   val1= args[1]->val_int();
00061   val1_unsigned= args[1]->unsigned_flag;
00062   if (val1 < 0)
00063     decimals_to_set= val1_unsigned ? INT_MAX : 0;
00064   else
00065     decimals_to_set= (val1 > INT_MAX) ? INT_MAX : (int) val1;
00066 
00067   if (args[0]->decimals == NOT_FIXED_DEC)
00068   {
00069     max_length= args[0]->max_length;
00070     decimals= min(decimals_to_set, (int)NOT_FIXED_DEC);
00071     hybrid_type= REAL_RESULT;
00072     return;
00073   }
00074 
00075   switch (args[0]->result_type()) {
00076   case REAL_RESULT:
00077   case STRING_RESULT:
00078     hybrid_type= REAL_RESULT;
00079     decimals= min(decimals_to_set, (int)NOT_FIXED_DEC);
00080     max_length= float_length(decimals);
00081     break;
00082   case INT_RESULT:
00083     if ((!decimals_to_set && truncate) || (args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS))
00084     {
00085       int length_can_increase= test(!truncate && (val1 < 0) && !val1_unsigned);
00086       max_length= args[0]->max_length + length_can_increase;
00087       /* Here we can keep INT_RESULT */
00088       hybrid_type= INT_RESULT;
00089       decimals= 0;
00090       break;
00091     }
00092     /* fall through */
00093   case DECIMAL_RESULT:
00094   {
00095     hybrid_type= DECIMAL_RESULT;
00096     decimals_to_set= min(DECIMAL_MAX_SCALE, decimals_to_set);
00097     int decimals_delta= args[0]->decimals - decimals_to_set;
00098     int precision= args[0]->decimal_precision();
00099     int length_increase= ((decimals_delta <= 0) || truncate) ? 0:1;
00100 
00101     precision-= decimals_delta - length_increase;
00102     decimals= min(decimals_to_set, DECIMAL_MAX_SCALE);
00103     max_length= class_decimal_precision_to_length(precision, decimals,
00104                                                unsigned_flag);
00105     break;
00106   }
00107   default:
00108     assert(0); /* This result type isn't handled */
00109   }
00110 }
00111 
00112 double my_double_round(double value, int64_t dec, bool dec_unsigned,
00113                        bool truncate)
00114 {
00115   double tmp;
00116   bool dec_negative= (dec < 0) && !dec_unsigned;
00117   uint64_t abs_dec= dec_negative ? -dec : dec;
00118   /*
00119     tmp2 is here to avoid return the value with 80 bit precision
00120     This will fix that the test round(0.1,1) = round(0.1,1) is true
00121   */
00122   double tmp2;
00123 
00124   tmp=(abs_dec < array_elements(log_10) ?
00125        log_10[abs_dec] : pow(10.0,(double) abs_dec));
00126 
00127   double value_times_tmp= value * tmp;
00128 
00129   /*
00130     NOTE: This is a workaround for a gcc 4.3 bug on Intel x86 32bit
00131     See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39228
00132     See http://bugs.mysql.com/bug.php?id=42965
00133 
00134     This forces the compiler to store/load the value as 64bit and avoids
00135     an optimisation that *could* have the infinite check be done on the 80bit
00136     representation.
00137    */
00138   if(sizeof(double) < sizeof(double_t))
00139   {
00140     volatile double t= value_times_tmp;
00141     value_times_tmp= t;
00142   }
00143 
00144   double infinity= numeric_limits<double>::infinity();
00145   if (dec_negative && (tmp == infinity))
00146     tmp2= 0;
00147   else if (!dec_negative && (value_times_tmp == infinity))
00148     tmp2= value;
00149   else if (truncate)
00150   {
00151     if (value >= 0)
00152       tmp2= dec < 0 ? floor(value/tmp)*tmp : floor(value*tmp)/tmp;
00153     else
00154       tmp2= dec < 0 ? ceil(value/tmp)*tmp : ceil(value*tmp)/tmp;
00155   }
00156   else
00157     tmp2=dec < 0 ? rint(value/tmp)*tmp : rint(value*tmp)/tmp;
00158   return tmp2;
00159 }
00160 
00161 
00162 double Item_func_round::real_op()
00163 {
00164   double value= args[0]->val_real();
00165 
00166   if (!(null_value= args[0]->null_value || args[1]->null_value))
00167     return my_double_round(value, args[1]->val_int(), args[1]->unsigned_flag,
00168                            truncate);
00169 
00170   return 0.0;
00171 }
00172 
00173 /*
00174   Rounds a given value to a power of 10 specified as the 'to' argument,
00175   avoiding overflows when the value is close to the uint64_t range boundary.
00176 */
00177 
00178 static inline uint64_t my_unsigned_round(uint64_t value, uint64_t to)
00179 {
00180   uint64_t tmp= value / to * to;
00181   return (value - tmp < (to >> 1)) ? tmp : tmp + to;
00182 }
00183 
00184 
00185 int64_t Item_func_round::int_op()
00186 {
00187   int64_t value= args[0]->val_int();
00188   int64_t dec= args[1]->val_int();
00189   decimals= 0;
00190   uint64_t abs_dec;
00191   if ((null_value= args[0]->null_value || args[1]->null_value))
00192     return 0;
00193   if ((dec >= 0) || args[1]->unsigned_flag)
00194     return value; // integer have not digits after point
00195 
00196   abs_dec= -dec;
00197   int64_t tmp;
00198 
00199   if(abs_dec >= array_elements(log_10_int))
00200     return 0;
00201 
00202   tmp= log_10_int[abs_dec];
00203 
00204   if (truncate)
00205     value= (unsigned_flag) ?
00206       (int64_t)(((uint64_t) value / tmp) * tmp) : (value / tmp) * tmp;
00207   else
00208     value= (unsigned_flag || value >= 0) ?
00209       (int64_t)(my_unsigned_round((uint64_t) value, tmp)) :
00210       -(int64_t) my_unsigned_round((uint64_t) -value, tmp);
00211   return value;
00212 }
00213 
00214 
00215 type::Decimal *Item_func_round::decimal_op(type::Decimal *decimal_value)
00216 {
00217   type::Decimal val, *value= args[0]->val_decimal(&val);
00218   int64_t dec= args[1]->val_int();
00219 
00220   if (dec >= 0 || args[1]->unsigned_flag)
00221     dec= min(dec, (int64_t) decimals);
00222   else if (dec < INT_MIN)
00223     dec= INT_MIN;
00224 
00225   if (!(null_value= (args[0]->null_value || args[1]->null_value ||
00226                      class_decimal_round(E_DEC_FATAL_ERROR, value, (int) dec,
00227                                       truncate, decimal_value) > 1)))
00228   {
00229     decimal_value->frac= decimals;
00230     return decimal_value;
00231   }
00232   return 0;
00233 }
00234 
00235 } /* namespace drizzled */