/* Copyright (C) 2000 MySQL AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DRIZZLED_DECIMAL_H #define DRIZZLED_DECIMAL_H #include #include #include "drizzled/definitions.h" #include "drizzled/drizzle_time.h" namespace drizzled { typedef enum {TRUNCATE=0, HALF_EVEN, HALF_UP, CEILING, FLOOR} decimal_round_mode; typedef int32_t decimal_digit_t; typedef struct st_decimal_t { int intg, frac, len; bool sign; decimal_digit_t *buf; } decimal_t; int internal_str2dec(char *from, decimal_t *to, char **end, bool fixed); int decimal2string(const decimal_t *from, char *to, int *to_len, int fixed_precision, int fixed_decimals, char filler); int decimal2uint64_t(const decimal_t *from, uint64_t *to); int uint64_t2decimal(const uint64_t from, decimal_t *to); int decimal2int64_t(const decimal_t *from, int64_t *to); int int64_t2decimal(const int64_t from, decimal_t *to); int decimal2double(const decimal_t *from, double *to); int double2decimal(const double from, decimal_t *to); int decimal_actual_fraction(decimal_t *from); int decimal2bin(const decimal_t *from, unsigned char *to, int precision, int scale); int bin2decimal(const unsigned char *from, decimal_t *to, int precision, int scale); int decimal_bin_size(int precision, int scale); int decimal_intg(const decimal_t *from); int decimal_add(const decimal_t *from1, const decimal_t *from2, decimal_t *to); int decimal_sub(const decimal_t *from1, const decimal_t *from2, decimal_t *to); int decimal_cmp(const decimal_t *from1, const decimal_t *from2); int decimal_mul(const decimal_t *from1, const decimal_t *from2, decimal_t *to); int decimal_div(const decimal_t *from1, const decimal_t *from2, decimal_t *to, int scale_incr); int decimal_mod(const decimal_t *from1, const decimal_t *from2, decimal_t *to); int decimal_round(const decimal_t *from, decimal_t *to, int new_scale, decimal_round_mode mode); int decimal_is_zero(const decimal_t *from); void max_decimal(int precision, int frac, decimal_t *to); inline int string2decimal(char *from, decimal_t *to, char **end) { return internal_str2dec(from, to, end, false); } /* set a decimal_t to zero */ inline void decimal_make_zero(decimal_t *dec) { dec->buf[0]=0; dec->intg=1; dec->frac=0; dec->sign=0; } /* returns the length of the buffer to hold string representation of the decimal (including decimal dot, possible sign and \0) */ inline int decimal_string_size(const decimal_t *dec) { return (dec->intg ? dec->intg : 1) + dec->frac + (dec->frac > 0) + 2; } /* negate a decimal */ inline void decimal_neg(decimal_t *dec) { dec->sign^=1; } /* conventions: decimal_smth() == 0 -- everything's ok decimal_smth() <= 1 -- result is usable, but precision loss is possible decimal_smth() <= 2 -- result can be unusable, most significant digits could've been lost decimal_smth() > 2 -- no result was generated */ #define E_DEC_OK 0 #define E_DEC_TRUNCATED 1 #define E_DEC_OVERFLOW 2 #define E_DEC_DIV_ZERO 4 #define E_DEC_BAD_NUM 8 #define E_DEC_OOM 16 #define E_DEC_ERROR 31 #define E_DEC_FATAL_ERROR 30 #define DECIMAL_LONGLONG_DIGITS 22 #define DECIMAL_LONG_DIGITS 10 #define DECIMAL_LONG3_DIGITS 8 /** maximum length of buffer in our big digits (uint32_t). */ #define DECIMAL_BUFF_LENGTH 9 /* the number of digits that my_decimal can possibly contain */ #define DECIMAL_MAX_POSSIBLE_PRECISION (DECIMAL_BUFF_LENGTH * 9) /** maximum guaranteed precision of number in decimal digits (number of our digits * number of decimal digits in one our big digit - number of decimal digits in one our big digit decreased by 1 (because we always put decimal point on the border of our big digits)) */ #define DECIMAL_MAX_PRECISION (DECIMAL_MAX_POSSIBLE_PRECISION - 8*2) #define DECIMAL_MAX_SCALE 30 #define DECIMAL_NOT_SPECIFIED 31 /** maximum length of string representation (number of maximum decimal digits + 1 position for sign + 1 position for decimal point) */ #define DECIMAL_MAX_STR_LENGTH (DECIMAL_MAX_POSSIBLE_PRECISION + 2) /** maximum size of packet length. */ #define DECIMAL_MAX_FIELD_SIZE DECIMAL_MAX_PRECISION inline int my_decimal_int_part(uint32_t precision, uint32_t decimals) { return precision - ((decimals == DECIMAL_NOT_SPECIFIED) ? 0 : decimals); } /** my_decimal class limits 'decimal_t' type to what we need in MySQL. It contains internally all necessary space needed by the instance so no extra memory is needed. One should call fix_buffer_pointer() function when he moves my_decimal objects in memory. */ class my_decimal :public decimal_t { decimal_digit_t buffer[DECIMAL_BUFF_LENGTH]; public: void init() { len= DECIMAL_BUFF_LENGTH; buf= buffer; #if !defined (HAVE_VALGRIND) /* Set buffer to 'random' value to find wrong buffer usage */ for (uint32_t i= 0; i < DECIMAL_BUFF_LENGTH; i++) buffer[i]= i; #endif } my_decimal() { init(); } void fix_buffer_pointer() { buf= buffer; } bool sign() const { return decimal_t::sign; } void sign(bool s) { decimal_t::sign= s; } uint32_t precision() const { return intg + frac; } }; int decimal_operation_results(int result); inline void max_my_decimal(my_decimal *to, int precision, int frac) { assert((precision <= DECIMAL_MAX_PRECISION)&& (frac <= DECIMAL_MAX_SCALE)); max_decimal(precision, frac, (decimal_t*) to); } inline void max_internal_decimal(my_decimal *to) { max_my_decimal(to, DECIMAL_MAX_PRECISION, 0); } inline int check_result(uint32_t mask, int result) { if (result & mask) decimal_operation_results(result); return result; } inline int check_result_and_overflow(uint32_t mask, int result, my_decimal *val) { if (check_result(mask, result) & E_DEC_OVERFLOW) { bool sign= val->sign(); val->fix_buffer_pointer(); max_internal_decimal(val); val->sign(sign); } return result; } inline uint32_t my_decimal_length_to_precision(uint32_t length, uint32_t scale, bool unsigned_flag) { return (uint32_t) (length - (scale>0 ? 1:0) - (unsigned_flag ? 0:1)); } inline uint32_t my_decimal_precision_to_length(uint32_t precision, uint8_t scale, bool unsigned_flag) { set_if_smaller(precision, (uint32_t)DECIMAL_MAX_PRECISION); return static_cast(precision + (scale>0 ? 1:0) + (unsigned_flag ? 0:1)); } inline int my_decimal_string_length(const my_decimal *d) { return decimal_string_size(d); } inline int my_decimal_max_length(const my_decimal *d) { /* -1 because we do not count \0 */ return decimal_string_size(d) - 1; } inline int my_decimal_get_binary_size(uint32_t precision, uint32_t scale) { return decimal_bin_size(static_cast(precision), static_cast(scale)); } inline void my_decimal2decimal(const my_decimal *from, my_decimal *to) { *to= *from; to->fix_buffer_pointer(); } int my_decimal2binary(uint32_t mask, const my_decimal *d, unsigned char *bin, int prec, int scale); inline int binary2my_decimal(uint32_t mask, const unsigned char *bin, my_decimal *d, int prec, int scale) { return check_result(mask, bin2decimal(bin, static_cast(d), prec, scale)); } inline int my_decimal_set_zero(my_decimal *d) { decimal_make_zero(static_cast (d)); return 0; } inline bool my_decimal_is_zero(const my_decimal *decimal_value) { return decimal_is_zero(static_cast(decimal_value)); } inline int my_decimal_round(uint32_t mask, const my_decimal *from, int scale, bool truncate, my_decimal *to) { return check_result(mask, decimal_round(static_cast(from), to, scale, (truncate ? TRUNCATE : HALF_UP))); } inline int my_decimal_floor(uint32_t mask, const my_decimal *from, my_decimal *to) { return check_result(mask, decimal_round(static_cast(from), to, 0, FLOOR)); } inline int my_decimal_ceiling(uint32_t mask, const my_decimal *from, my_decimal *to) { return check_result(mask, decimal_round(static_cast(from), to, 0, CEILING)); } int my_decimal2string(uint32_t mask, const my_decimal *d, uint32_t fixed_prec, uint32_t fixed_dec, char filler, String *str); inline int my_decimal2int(uint32_t mask, const my_decimal *d, bool unsigned_flag, int64_t *l) { my_decimal rounded; /* decimal_round can return only E_DEC_TRUNCATED */ decimal_round(static_cast(d), &rounded, 0, HALF_UP); return check_result(mask, (unsigned_flag ? decimal2uint64_t(&rounded, reinterpret_cast(l)) : decimal2int64_t(&rounded, l))); } inline int my_decimal2double(uint32_t, const my_decimal *d, double *result) { /* No need to call check_result as this will always succeed */ return decimal2double(static_cast(d), result); } inline int str2my_decimal(uint32_t mask, char *str, my_decimal *d, char **end) { return check_result_and_overflow(mask, string2decimal(str, static_cast(d),end), d); } int str2my_decimal(uint32_t mask, const char *from, uint32_t length, const CHARSET_INFO * charset, my_decimal *decimal_value); inline int string2my_decimal(uint32_t mask, const String *str, my_decimal *d) { return str2my_decimal(mask, str->ptr(), str->length(), str->charset(), d); } my_decimal *date2my_decimal(DRIZZLE_TIME *ltime, my_decimal *dec); inline int double2my_decimal(uint32_t mask, double val, my_decimal *d) { return check_result_and_overflow(mask, double2decimal(val, static_cast(d)), d); } inline int int2my_decimal(uint32_t mask, int64_t i, bool unsigned_flag, my_decimal *d) { return check_result(mask, (unsigned_flag ? uint64_t2decimal(static_cast(i), d) : int64_t2decimal(i, d))); } inline void my_decimal_neg(decimal_t *arg) { if (decimal_is_zero(arg)) { arg->sign= 0; return; } decimal_neg(arg); } inline int my_decimal_add(uint32_t mask, my_decimal *res, const my_decimal *a, const my_decimal *b) { return check_result_and_overflow(mask, decimal_add(static_cast(a), static_cast(b), res), res); } inline int my_decimal_sub(uint32_t mask, my_decimal *res, const my_decimal *a, const my_decimal *b) { return check_result_and_overflow(mask, decimal_sub(static_cast(a), static_cast(b), res), res); } inline int my_decimal_mul(uint32_t mask, my_decimal *res, const my_decimal *a, const my_decimal *b) { return check_result_and_overflow(mask, decimal_mul(static_cast(a), static_cast(b),res), res); } inline int my_decimal_div(uint32_t mask, my_decimal *res, const my_decimal *a, const my_decimal *b, int div_scale_inc) { return check_result_and_overflow(mask, decimal_div(static_cast(a), static_cast(b),res, div_scale_inc), res); } inline int my_decimal_mod(uint32_t mask, my_decimal *res, const my_decimal *a, const my_decimal *b) { return check_result_and_overflow(mask, decimal_mod(static_cast(a), static_cast(b),res), res); } /** @return -1 if ab and 0 if a==b */ inline int my_decimal_cmp(const my_decimal *a, const my_decimal *b) { return decimal_cmp(static_cast(a), static_cast(b)); } inline int my_decimal_intg(const my_decimal *a) { return decimal_intg(static_cast(a)); } void my_decimal_trim(uint32_t *precision, uint32_t *scale); } /* namespace drizzled */ #endif /* DRIZZLED_DECIMAL_H */