~drizzle-trunk/drizzle/development

492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
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
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 <drizzled/server_includes.h>
642.1.30 by Lee
move math functions to drizzled/function/math directory
21
#include <drizzled/function/math/round.h>
919.2.11 by Monty Taylor
Removed C99 isnan() usage, which allows us to remove the util/math.{cc,h} workarounds. Yay for standards!
22
23
#include <limits>
1067.4.2 by Nathan Williams
All files in drizzled/function/ now use std::min instead of cmin.
24
#include <algorithm>
919.2.11 by Monty Taylor
Removed C99 isnan() usage, which allows us to remove the util/math.{cc,h} workarounds. Yay for standards!
25
572.1.4 by Monty Taylor
Removed a bunch of unusued tests and defines from autoconf.
26
using namespace std;
492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
27
28
void Item_func_round::fix_length_and_dec()
29
{
30
  int      decimals_to_set;
31
  int64_t val1;
32
  bool     val1_unsigned;
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
33
492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
34
  unsigned_flag= args[0]->unsigned_flag;
35
  if (!args[1]->const_item())
36
  {
37
    max_length= args[0]->max_length;
38
    decimals= args[0]->decimals;
39
    if (args[0]->result_type() == DECIMAL_RESULT)
40
    {
41
      max_length++;
42
      hybrid_type= DECIMAL_RESULT;
43
    }
44
    else
45
      hybrid_type= REAL_RESULT;
46
    return;
47
  }
48
49
  val1= args[1]->val_int();
50
  val1_unsigned= args[1]->unsigned_flag;
51
  if (val1 < 0)
52
    decimals_to_set= val1_unsigned ? INT_MAX : 0;
53
  else
54
    decimals_to_set= (val1 > INT_MAX) ? INT_MAX : (int) val1;
55
56
  if (args[0]->decimals == NOT_FIXED_DEC)
57
  {
58
    max_length= args[0]->max_length;
1067.4.2 by Nathan Williams
All files in drizzled/function/ now use std::min instead of cmin.
59
    decimals= min(decimals_to_set, (int)NOT_FIXED_DEC);
492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
60
    hybrid_type= REAL_RESULT;
61
    return;
62
  }
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
63
492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
64
  switch (args[0]->result_type()) {
65
  case REAL_RESULT:
66
  case STRING_RESULT:
67
    hybrid_type= REAL_RESULT;
1067.4.2 by Nathan Williams
All files in drizzled/function/ now use std::min instead of cmin.
68
    decimals= min(decimals_to_set, (int)NOT_FIXED_DEC);
492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
69
    max_length= float_length(decimals);
70
    break;
71
  case INT_RESULT:
72
    if ((!decimals_to_set && truncate) || (args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS))
73
    {
74
      int length_can_increase= test(!truncate && (val1 < 0) && !val1_unsigned);
75
      max_length= args[0]->max_length + length_can_increase;
76
      /* Here we can keep INT_RESULT */
77
      hybrid_type= INT_RESULT;
78
      decimals= 0;
79
      break;
80
    }
81
    /* fall through */
82
  case DECIMAL_RESULT:
83
  {
84
    hybrid_type= DECIMAL_RESULT;
1067.4.2 by Nathan Williams
All files in drizzled/function/ now use std::min instead of cmin.
85
    decimals_to_set= min(DECIMAL_MAX_SCALE, decimals_to_set);
492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
86
    int decimals_delta= args[0]->decimals - decimals_to_set;
87
    int precision= args[0]->decimal_precision();
88
    int length_increase= ((decimals_delta <= 0) || truncate) ? 0:1;
89
90
    precision-= decimals_delta - length_increase;
1067.4.2 by Nathan Williams
All files in drizzled/function/ now use std::min instead of cmin.
91
    decimals= min(decimals_to_set, DECIMAL_MAX_SCALE);
492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
92
    max_length= my_decimal_precision_to_length(precision, decimals,
93
                                               unsigned_flag);
94
    break;
95
  }
96
  default:
97
    assert(0); /* This result type isn't handled */
98
  }
99
}
100
101
double my_double_round(double value, int64_t dec, bool dec_unsigned,
102
                       bool truncate)
103
{
104
  double tmp;
105
  bool dec_negative= (dec < 0) && !dec_unsigned;
106
  uint64_t abs_dec= dec_negative ? -dec : dec;
107
  /*
108
    tmp2 is here to avoid return the value with 80 bit precision
109
    This will fix that the test round(0.1,1) = round(0.1,1) is true
110
  */
869.1.16 by Stewart Smith
fix func_math on x86 32bit.
111
  double tmp2;
492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
112
113
  tmp=(abs_dec < array_elements(log_10) ?
114
       log_10[abs_dec] : pow(10.0,(double) abs_dec));
115
869.1.16 by Stewart Smith
fix func_math on x86 32bit.
116
  double value_times_tmp= value * tmp;
117
118
  /*
119
    NOTE: This is a workaround for a gcc 4.3 bug on Intel x86 32bit
120
    See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39228
121
    See http://bugs.mysql.com/bug.php?id=42965
122
123
    This forces the compiler to store/load the value as 64bit and avoids
919.2.9 by Monty Taylor
Replaced C99 isinf() with C++ numeric_traits<>::infinity()
124
    an optimisation that *could* have the infinite check be done on the 80bit
869.1.16 by Stewart Smith
fix func_math on x86 32bit.
125
    representation.
126
   */
127
  if(sizeof(double) < sizeof(double_t))
128
  {
129
    volatile double t= value_times_tmp;
130
    value_times_tmp= t;
131
  }
132
919.2.9 by Monty Taylor
Replaced C99 isinf() with C++ numeric_traits<>::infinity()
133
  double infinity= numeric_limits<double>::infinity();
134
  if (dec_negative && (tmp == infinity))
492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
135
    tmp2= 0;
919.2.9 by Monty Taylor
Replaced C99 isinf() with C++ numeric_traits<>::infinity()
136
  else if (!dec_negative && (value_times_tmp == infinity))
492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
137
    tmp2= value;
138
  else if (truncate)
139
  {
140
    if (value >= 0)
141
      tmp2= dec < 0 ? floor(value/tmp)*tmp : floor(value*tmp)/tmp;
142
    else
143
      tmp2= dec < 0 ? ceil(value/tmp)*tmp : ceil(value*tmp)/tmp;
144
  }
145
  else
146
    tmp2=dec < 0 ? rint(value/tmp)*tmp : rint(value*tmp)/tmp;
147
  return tmp2;
148
}
149
150
151
double Item_func_round::real_op()
152
{
153
  double value= args[0]->val_real();
154
155
  if (!(null_value= args[0]->null_value || args[1]->null_value))
156
    return my_double_round(value, args[1]->val_int(), args[1]->unsigned_flag,
157
                           truncate);
158
159
  return 0.0;
160
}
161
162
/*
163
  Rounds a given value to a power of 10 specified as the 'to' argument,
164
  avoiding overflows when the value is close to the uint64_t range boundary.
165
*/
166
167
static inline uint64_t my_unsigned_round(uint64_t value, uint64_t to)
168
{
169
  uint64_t tmp= value / to * to;
170
  return (value - tmp < (to >> 1)) ? tmp : tmp + to;
171
}
172
173
174
int64_t Item_func_round::int_op()
175
{
176
  int64_t value= args[0]->val_int();
177
  int64_t dec= args[1]->val_int();
178
  decimals= 0;
179
  uint64_t abs_dec;
180
  if ((null_value= args[0]->null_value || args[1]->null_value))
181
    return 0;
182
  if ((dec >= 0) || args[1]->unsigned_flag)
183
    return value; // integer have not digits after point
184
185
  abs_dec= -dec;
186
  int64_t tmp;
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
187
492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
188
  if(abs_dec >= array_elements(log_10_int))
189
    return 0;
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
190
492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
191
  tmp= log_10_int[abs_dec];
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
192
492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
193
  if (truncate)
194
    value= (unsigned_flag) ?
195
      ((uint64_t) value / tmp) * tmp : (value / tmp) * tmp;
196
  else
197
    value= (unsigned_flag || value >= 0) ?
198
      my_unsigned_round((uint64_t) value, tmp) :
199
      -(int64_t) my_unsigned_round((uint64_t) -value, tmp);
200
  return value;
201
}
202
203
204
my_decimal *Item_func_round::decimal_op(my_decimal *decimal_value)
205
{
206
  my_decimal val, *value= args[0]->val_decimal(&val);
207
  int64_t dec= args[1]->val_int();
1067.4.2 by Nathan Williams
All files in drizzled/function/ now use std::min instead of cmin.
208
492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
209
  if (dec >= 0 || args[1]->unsigned_flag)
1067.4.2 by Nathan Williams
All files in drizzled/function/ now use std::min instead of cmin.
210
    dec= min(dec, (int64_t) decimals);
492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
211
  else if (dec < INT_MIN)
212
    dec= INT_MIN;
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
213
492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
214
  if (!(null_value= (args[0]->null_value || args[1]->null_value ||
215
                     my_decimal_round(E_DEC_FATAL_ERROR, value, (int) dec,
660.1.3 by Eric Herman
removed trailing whitespace with simple script:
216
                                      truncate, decimal_value) > 1)))
492.3.13 by Lee
code clean move Item_func_floor, Item_func_length, Item_func_min_max, Item_func_rand, Item_func_round to functions directory
217
  {
218
    decimal_value->frac= decimals;
219
    return decimal_value;
220
  }
221
  return 0;
222
}
223