~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to server/field/double.cc

Merge work from Toru

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 MySQL
 
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; either version 2 of the License, or
 
9
 *  (at your option) any later version.
 
10
 *
 
11
 *  This program is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *  GNU General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU General Public License
 
17
 *  along with this program; if not, write to the Free Software
 
18
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
19
 */
 
20
 
 
21
#ifdef USE_PRAGMA_IMPLEMENTATION
 
22
#pragma implementation                          // gcc: Class implementation
 
23
#endif
 
24
 
 
25
#include "double.h"
 
26
 
 
27
/****************************************************************************
 
28
  double precision floating point numbers
 
29
****************************************************************************/
 
30
 
 
31
int Field_double::store(const char *from,uint len,CHARSET_INFO *cs)
 
32
{
 
33
  int error;
 
34
  char *end;
 
35
  double nr= my_strntod(cs,(char*) from, len, &end, &error);
 
36
  if (error || (!len || (((uint) (end-from) != len) && table->in_use->count_cuted_fields)))
 
37
  {
 
38
    set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
 
39
                (error ? ER_WARN_DATA_OUT_OF_RANGE : WARN_DATA_TRUNCATED), 1);
 
40
    error= error ? 1 : 2;
 
41
  }
 
42
  Field_double::store(nr);
 
43
  return error;
 
44
}
 
45
 
 
46
 
 
47
int Field_double::store(double nr)
 
48
{
 
49
  int error= truncate(&nr, DBL_MAX);
 
50
 
 
51
#ifdef WORDS_BIGENDIAN
 
52
  if (table->s->db_low_byte_first)
 
53
  {
 
54
    float8store(ptr,nr);
 
55
  }
 
56
  else
 
57
#endif
 
58
    doublestore(ptr,nr);
 
59
  return error;
 
60
}
 
61
 
 
62
 
 
63
int Field_double::store(int64_t nr, bool unsigned_val)
 
64
{
 
65
  return Field_double::store(unsigned_val ? uint64_t2double((uint64_t) nr) :
 
66
                             (double) nr);
 
67
}
 
68
 
 
69
/*
 
70
  If a field has fixed length, truncate the double argument pointed to by 'nr'
 
71
  appropriately.
 
72
  Also ensure that the argument is within [-max_value; max_value] range.
 
73
*/
 
74
 
 
75
int Field_real::truncate(double *nr, double max_value)
 
76
{
 
77
  int error= 1;
 
78
  double res= *nr;
 
79
  
 
80
  if (isnan(res))
 
81
  {
 
82
    res= 0;
 
83
    set_null();
 
84
    set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
 
85
    goto end;
 
86
  }
 
87
  else if (unsigned_flag && res < 0)
 
88
  {
 
89
    res= 0;
 
90
    set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
 
91
    goto end;
 
92
  }
 
93
 
 
94
  if (!not_fixed)
 
95
  {
 
96
    uint order= field_length - dec;
 
97
    uint step= array_elements(log_10) - 1;
 
98
    max_value= 1.0;
 
99
    for (; order > step; order-= step)
 
100
      max_value*= log_10[step];
 
101
    max_value*= log_10[order];
 
102
    max_value-= 1.0 / log_10[dec];
 
103
 
 
104
    /* Check for infinity so we don't get NaN in calculations */
 
105
    if (!my_isinf(res))
 
106
    {
 
107
      double tmp= rint((res - floor(res)) * log_10[dec]) / log_10[dec];
 
108
      res= floor(res) + tmp;
 
109
    }
 
110
  }
 
111
  
 
112
  if (res < -max_value)
 
113
  {
 
114
   res= -max_value;
 
115
   set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
 
116
  }
 
117
  else if (res > max_value)
 
118
  {
 
119
    res= max_value;
 
120
    set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
 
121
  }
 
122
  else
 
123
    error= 0;
 
124
 
 
125
end:
 
126
  *nr= res;
 
127
  return error;
 
128
}
 
129
 
 
130
 
 
131
int Field_real::store_decimal(const my_decimal *dm)
 
132
{
 
133
  double dbl;
 
134
  my_decimal2double(E_DEC_FATAL_ERROR, dm, &dbl);
 
135
  return store(dbl);
 
136
}
 
137
 
 
138
double Field_double::val_real(void)
 
139
{
 
140
  double j;
 
141
#ifdef WORDS_BIGENDIAN
 
142
  if (table->s->db_low_byte_first)
 
143
  {
 
144
    float8get(j,ptr);
 
145
  }
 
146
  else
 
147
#endif
 
148
    doubleget(j,ptr);
 
149
  return j;
 
150
}
 
151
 
 
152
int64_t Field_double::val_int(void)
 
153
{
 
154
  double j;
 
155
  int64_t res;
 
156
#ifdef WORDS_BIGENDIAN
 
157
  if (table->s->db_low_byte_first)
 
158
  {
 
159
    float8get(j,ptr);
 
160
  }
 
161
  else
 
162
#endif
 
163
    doubleget(j,ptr);
 
164
  /* Check whether we fit into int64_t range */
 
165
  if (j <= (double) INT64_MIN)
 
166
  {
 
167
    res= (int64_t) INT64_MIN;
 
168
    goto warn;
 
169
  }
 
170
  if (j >= (double) (uint64_t) INT64_MAX)
 
171
  {
 
172
    res= (int64_t) INT64_MAX;
 
173
    goto warn;
 
174
  }
 
175
  return (int64_t) rint(j);
 
176
 
 
177
warn:
 
178
  {
 
179
    char buf[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
 
180
    String tmp(buf, sizeof(buf), &my_charset_latin1), *str;
 
181
    str= val_str(&tmp, 0);
 
182
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
183
                        ER_TRUNCATED_WRONG_VALUE,
 
184
                        ER(ER_TRUNCATED_WRONG_VALUE), "INTEGER",
 
185
                        str->c_ptr());
 
186
  }
 
187
  return res;
 
188
}
 
189
 
 
190
 
 
191
my_decimal *Field_real::val_decimal(my_decimal *decimal_value)
 
192
{
 
193
  double2my_decimal(E_DEC_FATAL_ERROR, val_real(), decimal_value);
 
194
  return decimal_value;
 
195
}
 
196
 
 
197
 
 
198
String *Field_double::val_str(String *val_buffer,
 
199
                              String *val_ptr __attribute__((unused)))
 
200
{
 
201
  double nr;
 
202
#ifdef WORDS_BIGENDIAN
 
203
  if (table->s->db_low_byte_first)
 
204
  {
 
205
    float8get(nr,ptr);
 
206
  }
 
207
  else
 
208
#endif
 
209
    doubleget(nr,ptr);
 
210
 
 
211
  uint to_length=max(field_length, DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE);
 
212
  val_buffer->alloc(to_length);
 
213
  char *to=(char*) val_buffer->ptr();
 
214
  size_t len;
 
215
 
 
216
  if (dec >= NOT_FIXED_DEC)
 
217
    len= my_gcvt(nr, MY_GCVT_ARG_DOUBLE, to_length - 1, to, NULL);
 
218
  else
 
219
    len= my_fcvt(nr, dec, to, NULL);
 
220
 
 
221
  val_buffer->length((uint) len);
 
222
  if (zerofill)
 
223
    prepend_zeros(val_buffer);
 
224
  return val_buffer;
 
225
}
 
226
 
 
227
bool Field_double::send_binary(Protocol *protocol)
 
228
{
 
229
  return protocol->store((double) Field_double::val_real(), dec, (String*) 0);
 
230
}
 
231
 
 
232
 
 
233
int Field_double::cmp(const uchar *a_ptr, const uchar *b_ptr)
 
234
{
 
235
  double a,b;
 
236
#ifdef WORDS_BIGENDIAN
 
237
  if (table->s->db_low_byte_first)
 
238
  {
 
239
    float8get(a,a_ptr);
 
240
    float8get(b,b_ptr);
 
241
  }
 
242
  else
 
243
#endif
 
244
  {
 
245
    doubleget(a, a_ptr);
 
246
    doubleget(b, b_ptr);
 
247
  }
 
248
  return (a < b) ? -1 : (a > b) ? 1 : 0;
 
249
}
 
250
 
 
251
 
 
252
#define DBL_EXP_DIG (sizeof(double)*8-DBL_MANT_DIG)
 
253
 
 
254
/* The following should work for IEEE */
 
255
 
 
256
void Field_double::sort_string(uchar *to,uint length __attribute__((unused)))
 
257
{
 
258
  double nr;
 
259
#ifdef WORDS_BIGENDIAN
 
260
  if (table->s->db_low_byte_first)
 
261
  {
 
262
    float8get(nr,ptr);
 
263
  }
 
264
  else
 
265
#endif
 
266
    doubleget(nr,ptr);
 
267
  change_double_for_sort(nr, to);
 
268
}
 
269
 
 
270
 
 
271
/**
 
272
   Save the field metadata for double fields.
 
273
 
 
274
   Saves the pack length in the first byte of the field metadata array
 
275
   at index of *metadata_ptr.
 
276
 
 
277
   @param   metadata_ptr   First byte of field metadata
 
278
 
 
279
   @returns number of bytes written to metadata_ptr
 
280
*/
 
281
int Field_double::do_save_field_metadata(uchar *metadata_ptr)
 
282
{
 
283
  *metadata_ptr= pack_length();
 
284
  return 1;
 
285
}
 
286
 
 
287
 
 
288
void Field_double::sql_type(String &res) const
 
289
{
 
290
  CHARSET_INFO *cs=res.charset();
 
291
  if (dec == NOT_FIXED_DEC)
 
292
  {
 
293
    res.set_ascii(STRING_WITH_LEN("double"));
 
294
  }
 
295
  else
 
296
  {
 
297
    res.length(cs->cset->snprintf(cs,(char*) res.ptr(),res.alloced_length(),
 
298
                            "double(%d,%d)",(int) field_length,dec));
 
299
  }
 
300
  add_zerofill_and_unsigned(res);
 
301
}
 
302