Drizzled Public API Documentation

round.cc
1 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3  *
4  * Copyright (C) 2008 Sun Microsystems, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 #include <config.h>
21 
22 #include <math.h>
23 #include <limits.h>
24 
25 #include <limits>
26 #include <algorithm>
27 
28 #include <drizzled/function/math/round.h>
29 #include <drizzled/util/test.h>
30 
31 namespace drizzled
32 {
33 
34 extern const double log_10[309];
35 
36 
37 using namespace std;
38 
39 void Item_func_round::fix_length_and_dec()
40 {
41  int decimals_to_set;
42  int64_t val1;
43  bool val1_unsigned;
44 
45  unsigned_flag= args[0]->unsigned_flag;
46  if (!args[1]->const_item())
47  {
48  max_length= args[0]->max_length;
49  decimals= args[0]->decimals;
50  if (args[0]->result_type() == DECIMAL_RESULT)
51  {
52  max_length++;
53  hybrid_type= DECIMAL_RESULT;
54  }
55  else
56  hybrid_type= REAL_RESULT;
57  return;
58  }
59 
60  val1= args[1]->val_int();
61  val1_unsigned= args[1]->unsigned_flag;
62  if (val1 < 0)
63  decimals_to_set= val1_unsigned ? INT_MAX : 0;
64  else
65  decimals_to_set= (val1 > INT_MAX) ? INT_MAX : (int) val1;
66 
67  if (args[0]->decimals == NOT_FIXED_DEC)
68  {
69  max_length= args[0]->max_length;
70  decimals= min(decimals_to_set, (int)NOT_FIXED_DEC);
71  hybrid_type= REAL_RESULT;
72  return;
73  }
74 
75  switch (args[0]->result_type()) {
76  case REAL_RESULT:
77  case STRING_RESULT:
78  hybrid_type= REAL_RESULT;
79  decimals= min(decimals_to_set, (int)NOT_FIXED_DEC);
80  max_length= float_length(decimals);
81  break;
82  case INT_RESULT:
83  if ((!decimals_to_set && truncate) || (args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS))
84  {
85  int length_can_increase= test(!truncate && (val1 < 0) && !val1_unsigned);
86  max_length= args[0]->max_length + length_can_increase;
87  /* Here we can keep INT_RESULT */
88  hybrid_type= INT_RESULT;
89  decimals= 0;
90  break;
91  }
92  /* fall through */
93  case DECIMAL_RESULT:
94  {
95  hybrid_type= DECIMAL_RESULT;
96  decimals_to_set= min(DECIMAL_MAX_SCALE, decimals_to_set);
97  int decimals_delta= args[0]->decimals - decimals_to_set;
98  int precision= args[0]->decimal_precision();
99  int length_increase= ((decimals_delta <= 0) || truncate) ? 0:1;
100 
101  precision-= decimals_delta - length_increase;
102  decimals= min(decimals_to_set, DECIMAL_MAX_SCALE);
103  max_length= class_decimal_precision_to_length(precision, decimals,
104  unsigned_flag);
105  break;
106  }
107  default:
108  assert(0); /* This result type isn't handled */
109  }
110 }
111 
112 double my_double_round(double value, int64_t dec, bool dec_unsigned,
113  bool truncate)
114 {
115  double tmp;
116  bool dec_negative= (dec < 0) && !dec_unsigned;
117  uint64_t abs_dec= dec_negative ? -dec : dec;
118  /*
119  tmp2 is here to avoid return the value with 80 bit precision
120  This will fix that the test round(0.1,1) = round(0.1,1) is true
121  */
122  double tmp2;
123 
124  tmp=(abs_dec < array_elements(log_10) ?
125  log_10[abs_dec] : pow(10.0,(double) abs_dec));
126 
127  double value_times_tmp= value * tmp;
128 
129  /*
130  NOTE: This is a workaround for a gcc 4.3 bug on Intel x86 32bit
131  See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39228
132  See http://bugs.mysql.com/bug.php?id=42965
133 
134  This forces the compiler to store/load the value as 64bit and avoids
135  an optimisation that *could* have the infinite check be done on the 80bit
136  representation.
137  */
138  if(sizeof(double) < sizeof(double_t))
139  {
140  volatile double t= value_times_tmp;
141  value_times_tmp= t;
142  }
143 
144  double infinity= numeric_limits<double>::infinity();
145  if (dec_negative && (tmp == infinity))
146  tmp2= 0;
147  else if (!dec_negative && (value_times_tmp == infinity))
148  tmp2= value;
149  else if (truncate)
150  {
151  if (value >= 0)
152  tmp2= dec < 0 ? floor(value/tmp)*tmp : floor(value*tmp)/tmp;
153  else
154  tmp2= dec < 0 ? ceil(value/tmp)*tmp : ceil(value*tmp)/tmp;
155  }
156  else
157  tmp2=dec < 0 ? rint(value/tmp)*tmp : rint(value*tmp)/tmp;
158  return tmp2;
159 }
160 
161 
163 {
164  double value= args[0]->val_real();
165 
166  if (!(null_value= args[0]->null_value || args[1]->null_value))
167  return my_double_round(value, args[1]->val_int(), args[1]->unsigned_flag,
168  truncate);
169 
170  return 0.0;
171 }
172 
173 /*
174  Rounds a given value to a power of 10 specified as the 'to' argument,
175  avoiding overflows when the value is close to the uint64_t range boundary.
176 */
177 
178 static inline uint64_t my_unsigned_round(uint64_t value, uint64_t to)
179 {
180  uint64_t tmp= value / to * to;
181  return (value - tmp < (to >> 1)) ? tmp : tmp + to;
182 }
183 
184 
186 {
187  int64_t value= args[0]->val_int();
188  int64_t dec= args[1]->val_int();
189  decimals= 0;
190  uint64_t abs_dec;
191  if ((null_value= args[0]->null_value || args[1]->null_value))
192  return 0;
193  if ((dec >= 0) || args[1]->unsigned_flag)
194  return value; // integer have not digits after point
195 
196  abs_dec= -dec;
197  int64_t tmp;
198 
199  if(abs_dec >= array_elements(log_10_int))
200  return 0;
201 
202  tmp= log_10_int[abs_dec];
203 
204  if (truncate)
205  value= (unsigned_flag) ?
206  (int64_t)(((uint64_t) value / tmp) * tmp) : (value / tmp) * tmp;
207  else
208  value= (unsigned_flag || value >= 0) ?
209  (int64_t)(my_unsigned_round((uint64_t) value, tmp)) :
210  -(int64_t) my_unsigned_round((uint64_t) -value, tmp);
211  return value;
212 }
213 
214 
216 {
217  type::Decimal val, *value= args[0]->val_decimal(&val);
218  int64_t dec= args[1]->val_int();
219 
220  if (dec >= 0 || args[1]->unsigned_flag)
221  dec= min(dec, (int64_t) decimals);
222  else if (dec < INT_MIN)
223  dec= INT_MIN;
224 
225  if (!(null_value= (args[0]->null_value || args[1]->null_value ||
226  class_decimal_round(E_DEC_FATAL_ERROR, value, (int) dec,
227  truncate, decimal_value) > 1)))
228  {
229  decimal_value->frac= decimals;
230  return decimal_value;
231  }
232  return 0;
233 }
234 
235 } /* namespace drizzled */
TODO: Rename this file - func.h is stupid.
int64_t int_op()
Performs the operation that this functions implements when the result type is INT.
Definition: round.cc:185
double real_op()
Performs the operation that this functions implements when the result type is REAL.
Definition: round.cc:162
type::Decimal * decimal_op(type::Decimal *)
Performs the operation that this functions implements when the result type is DECIMAL.
Definition: round.cc:215