~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/function/math/round.cc

  • Committer: Monty Taylor
  • Date: 2008-09-16 00:00:48 UTC
  • mto: This revision was merged to the branch mainline in revision 391.
  • Revision ID: monty@inaugust.com-20080916000048-3rvrv3gv9l0ad3gs
Fixed copyright headers in drizzled/

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
 
162
 
double Item_func_round::real_op()
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
 
 
185
 
int64_t Item_func_round::int_op()
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
 
 
215
 
type::Decimal *Item_func_round::decimal_op(type::Decimal *decimal_value)
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 */