1
/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
4
* Copyright (C) 2008 Sun Microsystems
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.
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.
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
20
#include <drizzled/server_includes.h>
22
#include <drizzled/function/math/round.h>
24
#include <drizzled/util/math.h>
26
#if defined(CMATH_NAMESPACE)
27
using namespace CMATH_NAMESPACE;
31
void Item_func_round::fix_length_and_dec()
37
unsigned_flag= args[0]->unsigned_flag;
38
if (!args[1]->const_item())
40
max_length= args[0]->max_length;
41
decimals= args[0]->decimals;
42
if (args[0]->result_type() == DECIMAL_RESULT)
45
hybrid_type= DECIMAL_RESULT;
48
hybrid_type= REAL_RESULT;
52
val1= args[1]->val_int();
53
val1_unsigned= args[1]->unsigned_flag;
55
decimals_to_set= val1_unsigned ? INT_MAX : 0;
57
decimals_to_set= (val1 > INT_MAX) ? INT_MAX : (int) val1;
59
if (args[0]->decimals == NOT_FIXED_DEC)
61
max_length= args[0]->max_length;
62
decimals= cmin(decimals_to_set, NOT_FIXED_DEC);
63
hybrid_type= REAL_RESULT;
67
switch (args[0]->result_type()) {
70
hybrid_type= REAL_RESULT;
71
decimals= cmin(decimals_to_set, NOT_FIXED_DEC);
72
max_length= float_length(decimals);
75
if ((!decimals_to_set && truncate) || (args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS))
77
int length_can_increase= test(!truncate && (val1 < 0) && !val1_unsigned);
78
max_length= args[0]->max_length + length_can_increase;
79
/* Here we can keep INT_RESULT */
80
hybrid_type= INT_RESULT;
87
hybrid_type= DECIMAL_RESULT;
88
decimals_to_set= cmin(DECIMAL_MAX_SCALE, decimals_to_set);
89
int decimals_delta= args[0]->decimals - decimals_to_set;
90
int precision= args[0]->decimal_precision();
91
int length_increase= ((decimals_delta <= 0) || truncate) ? 0:1;
93
precision-= decimals_delta - length_increase;
94
decimals= cmin(decimals_to_set, DECIMAL_MAX_SCALE);
95
max_length= my_decimal_precision_to_length(precision, decimals,
100
assert(0); /* This result type isn't handled */
104
double my_double_round(double value, int64_t dec, bool dec_unsigned,
108
bool dec_negative= (dec < 0) && !dec_unsigned;
109
uint64_t abs_dec= dec_negative ? -dec : dec;
111
tmp2 is here to avoid return the value with 80 bit precision
112
This will fix that the test round(0.1,1) = round(0.1,1) is true
116
tmp=(abs_dec < array_elements(log_10) ?
117
log_10[abs_dec] : pow(10.0,(double) abs_dec));
119
double value_times_tmp= value * tmp;
122
NOTE: This is a workaround for a gcc 4.3 bug on Intel x86 32bit
123
See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39228
124
See http://bugs.mysql.com/bug.php?id=42965
126
This forces the compiler to store/load the value as 64bit and avoids
127
an optimisation that *could* have the isinf() be done on the 80bit
130
if(sizeof(double) < sizeof(double_t))
132
volatile double t= value_times_tmp;
136
if (dec_negative && isinf(tmp))
138
else if (!dec_negative && isinf(value_times_tmp))
143
tmp2= dec < 0 ? floor(value/tmp)*tmp : floor(value*tmp)/tmp;
145
tmp2= dec < 0 ? ceil(value/tmp)*tmp : ceil(value*tmp)/tmp;
148
tmp2=dec < 0 ? rint(value/tmp)*tmp : rint(value*tmp)/tmp;
153
double Item_func_round::real_op()
155
double value= args[0]->val_real();
157
if (!(null_value= args[0]->null_value || args[1]->null_value))
158
return my_double_round(value, args[1]->val_int(), args[1]->unsigned_flag,
165
Rounds a given value to a power of 10 specified as the 'to' argument,
166
avoiding overflows when the value is close to the uint64_t range boundary.
169
static inline uint64_t my_unsigned_round(uint64_t value, uint64_t to)
171
uint64_t tmp= value / to * to;
172
return (value - tmp < (to >> 1)) ? tmp : tmp + to;
176
int64_t Item_func_round::int_op()
178
int64_t value= args[0]->val_int();
179
int64_t dec= args[1]->val_int();
182
if ((null_value= args[0]->null_value || args[1]->null_value))
184
if ((dec >= 0) || args[1]->unsigned_flag)
185
return value; // integer have not digits after point
190
if(abs_dec >= array_elements(log_10_int))
193
tmp= log_10_int[abs_dec];
196
value= (unsigned_flag) ?
197
((uint64_t) value / tmp) * tmp : (value / tmp) * tmp;
199
value= (unsigned_flag || value >= 0) ?
200
my_unsigned_round((uint64_t) value, tmp) :
201
-(int64_t) my_unsigned_round((uint64_t) -value, tmp);
206
my_decimal *Item_func_round::decimal_op(my_decimal *decimal_value)
208
my_decimal val, *value= args[0]->val_decimal(&val);
209
int64_t dec= args[1]->val_int();
210
if (dec >= 0 || args[1]->unsigned_flag)
211
dec= cmin(dec, (int64_t) decimals);
212
else if (dec < INT_MIN)
215
if (!(null_value= (args[0]->null_value || args[1]->null_value ||
216
my_decimal_round(E_DEC_FATAL_ERROR, value, (int) dec,
217
truncate, decimal_value) > 1)))
219
decimal_value->frac= decimals;
220
return decimal_value;