diff options
Diffstat (limited to 'strings/decimal.c')
-rw-r--r-- | strings/decimal.c | 1165 |
1 files changed, 923 insertions, 242 deletions
diff --git a/strings/decimal.c b/strings/decimal.c index 42e65c26ad1..937ceb2f59b 100644 --- a/strings/decimal.c +++ b/strings/decimal.c @@ -113,11 +113,16 @@ typedef longlong dec2; #define DIG_PER_DEC1 9 #define DIG_MASK 100000000 #define DIG_BASE 1000000000 +#define DIG_MAX 999999999 #define DIG_BASE2 LL(1000000000000000000) #define ROUND_UP(X) (((X)+DIG_PER_DEC1-1)/DIG_PER_DEC1) static const dec1 powers10[DIG_PER_DEC1+1]={ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; static const int dig2bytes[DIG_PER_DEC1+1]={0, 1, 1, 2, 2, 3, 3, 4, 4, 4}; +static const dec1 frac_max[DIG_PER_DEC1-1]={ + 900000000, 990000000, 999000000, + 999900000, 999990000, 999999000, + 999999900, 999999990 }; #define sanity(d) DBUG_ASSERT((d)->len >0 && ((d)->buf[0] | \ (d)->buf[(d)->len-1] | 1)) @@ -190,43 +195,144 @@ static const int dig2bytes[DIG_PER_DEC1+1]={0, 1, 1, 2, 2, 3, 3, 4, 4, 4}; } while(0) /* - Convert decimal to its printable string representation + Get maximum value for given precision and scale SYNOPSIS - decimal2string() - from - value to convert - to - points to buffer where string representation should be stored - *to_len - in: size of to buffer - out: length of the actually written string - - RETURN VALUE - E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW + max_decimal() + precision/scale - see decimal_bin_size() below + to - decimal where where the result will be stored + to->buf and to->len must be set. */ -int decimal2string(decimal *from, char *to, int *to_len) +void max_decimal(int precision, int frac, decimal *to) { - int len, intg=from->intg, frac=from->frac, i; - int error=E_DEC_OK; - char *s=to; - dec1 *buf, *buf0=from->buf, tmp; + int intpart; + dec1 *buf= to->buf; + DBUG_ASSERT(precision && precision >= frac); - DBUG_ASSERT(*to_len >= 2+from->sign); + to->sign= 0; + if ((intpart= to->intg= (precision - frac))) + { + int firstdigits= intpart % DIG_PER_DEC1; + if (firstdigits) + *buf++= powers10[firstdigits] - 1; /* get 9 99 999 ... */ + for(intpart/= DIG_PER_DEC1; intpart; intpart--) + *buf++= DIG_MAX; + } - /* removing leading zeroes */ - i=((intg-1) % DIG_PER_DEC1)+1; + if ((to->frac= frac)) + { + int lastdigits= frac % DIG_PER_DEC1; + for(frac/= DIG_PER_DEC1; frac; frac--) + *buf++= DIG_MAX; + if (lastdigits) + *buf= frac_max[lastdigits - 1]; + } +} + + +static dec1 *remove_leading_zeroes(decimal *from, int *intg_result) +{ + int intg= from->intg, i; + dec1 *buf0= from->buf; + i= ((intg - 1) % DIG_PER_DEC1) + 1; while (intg > 0 && *buf0 == 0) { - intg-=i; - i=DIG_PER_DEC1; + intg-= i; + i= DIG_PER_DEC1; buf0++; } if (intg > 0) { - for (i=(intg-1) % DIG_PER_DEC1; *buf0 < powers10[i--]; intg--) ; + for (i= (intg - 1) % DIG_PER_DEC1; *buf0 < powers10[i--]; intg--) ; DBUG_ASSERT(intg > 0); } else intg=0; + *intg_result= intg; + return buf0; +} + + +/* + Remove ending 0 digits from fraction part + + SYNOPSIS + decimal_optimize_fraction() + from number for processing +*/ + +void decimal_optimize_fraction(decimal *from) +{ + int frac= from->frac, i; + dec1 *buf0= from->buf + ROUND_UP(from->intg) + ROUND_UP(frac) - 1; + + if (frac == 0) + return; + + i= ((frac - 1) % DIG_PER_DEC1 + 1); + while (frac > 0 && *buf0 == 0) + { + frac-= i; + i= DIG_PER_DEC1; + buf0--; + } + if (frac > 0) + { + for (i= DIG_PER_DEC1 - ((frac - 1) % DIG_PER_DEC1); + *buf0 % powers10[i++] == 0; + frac--); + } + from->frac= frac; +} + + +/* + Convert decimal to its printable string representation + + SYNOPSIS + decimal2string() + from - value to convert + to - points to buffer where string representation + should be stored + *to_len - in: size of to buffer + out: length of the actually written string + fixed_precision - 0 if representation can be variable length and + fixed_decimals will not be checked in this case. + Put number as with fixed point position with this + number of digits (sign counted and decimal point is + counted) + fixed_decimals - number digits after point. + filler - character to fill gaps in case of fixed_precision > 0 + + RETURN VALUE + E_DEC_OK/E_DEC_TRUNCATED/E_DEC_OVERFLOW +*/ + +int decimal2string(decimal *from, char *to, int *to_len, + int fixed_precision, int fixed_decimals, + char filler) +{ + int len, intg, frac=from->frac, i, intg_len, frac_len, fill; + /* number digits before decimal point */ + int fixed_intg= (fixed_precision ? + (fixed_precision - + (from->sign ? 1 : 0) - + (fixed_decimals ? 1 : 0) - + fixed_decimals) : + 0); + int error=E_DEC_OK; + char *s=to; + dec1 *buf, *buf0=from->buf, tmp; + + DBUG_ASSERT(*to_len >= 2+from->sign); + DBUG_ASSERT(fixed_precision == 0 || + (fixed_precision < *to_len && + fixed_precision > ((from->sign ? 1 : 0) + + (fixed_decimals ? 1 : 0)))); + + /* removing leading zeroes */ + buf0= remove_leading_zeroes(from, &intg); if (unlikely(intg+frac==0)) { intg=1; @@ -234,8 +340,23 @@ int decimal2string(decimal *from, char *to, int *to_len) buf0=&tmp; } - len= from->sign + intg + test(frac) + frac; - if (unlikely(len > --*to_len)) /* reserve one byte for \0 */ + intg_len= fixed_precision ? fixed_intg : (intg ? intg : 1); + frac_len= fixed_precision ? fixed_decimals : frac; + len= from->sign + intg_len + test(frac) + frac_len; + if (fixed_precision) + { + if (frac > fixed_decimals) + { + error= E_DEC_TRUNCATED; + frac= fixed_decimals; + } + if (intg > fixed_intg) + { + error= E_DEC_OVERFLOW; + intg= fixed_intg; + } + } + else if (unlikely(len > --*to_len)) /* reserve one byte for \0 */ { int i=len-*to_len; error= (frac && i <= frac + 1) ? E_DEC_TRUNCATED : E_DEC_OVERFLOW; @@ -247,7 +368,7 @@ int decimal2string(decimal *from, char *to, int *to_len) } else frac-=i; - len= from->sign + intg + test(frac) + frac; + len= from->sign + intg_len + test(frac) + frac_len; } *to_len=len; s[len]=0; @@ -257,7 +378,8 @@ int decimal2string(decimal *from, char *to, int *to_len) if (frac) { - char *s1=s+intg; + char *s1= s + intg_len; + fill= frac_len - frac; buf=buf0+ROUND_UP(intg); *s1++='.'; for (; frac>0; frac-=DIG_PER_DEC1) @@ -271,22 +393,351 @@ int decimal2string(decimal *from, char *to, int *to_len) x*=10; } } + for(; fill; fill--) + *s1++=filler; } - s+=intg; - for (buf=buf0+ROUND_UP(intg); intg>0; intg-=DIG_PER_DEC1) + fill= intg_len - intg; + if (intg == 0) + fill--; /* symbol 0 before digital point */ + for(; fill; fill--) + *s++=filler; + if (intg) { - dec1 x=*--buf; - for (i=min(intg, DIG_PER_DEC1); i; i--) + s+=intg; + for (buf=buf0+ROUND_UP(intg); intg>0; intg-=DIG_PER_DEC1) { - dec1 y=x/10; - *--s='0'+(uchar)(x-y*10); - x=y; + dec1 x=*--buf; + for (i=min(intg, DIG_PER_DEC1); i; i--) + { + dec1 y=x/10; + *--s='0'+(uchar)(x-y*10); + x=y; + } } } + else + *s= '0'; return error; } + +/* + Return bounds of decimal digits in the number + + SYNOPSIS + digits_bounds() + from - decimal number for processing + start_result - index (from 0 ) of first decimal digits will + be written by this address + end_result - index of position just after last decimal digit + be written by this address +*/ + +static void digits_bounds(decimal *from, int *start_result, int *end_result) +{ + int start, stop, i; + dec1 *buf_beg= from->buf; + dec1 *end= from->buf + ROUND_UP(from->intg) + ROUND_UP(from->frac); + dec1 *buf_end= end - 1; + + /* find non-zero digit from number begining */ + while (buf_beg < end && *buf_beg == 0) + buf_beg++; + + if (buf_beg >= end) + { + /* it is zero */ + *start_result= *end_result= 0; + return; + } + + /* find non-zero decimal digit from number begining */ + if (buf_beg == from->buf && from->intg) + { + start= DIG_PER_DEC1 - (i= ((from->intg-1) % DIG_PER_DEC1 + 1)); + i--; + } + else + { + i= DIG_PER_DEC1 - 1; + start= (buf_beg - from->buf) * DIG_PER_DEC1; + } + if (buf_beg < end) + for (; *buf_beg < powers10[i--]; start++) ; + *start_result= start; /* index of first decimal digit (from 0) */ + + /* find non-zero digit at the end */ + while (buf_end > buf_beg && *buf_end == 0) + buf_end--; + /* find non-zero decimal digit from the end */ + if (buf_end == end - 1 && from->frac) + { + stop= ((buf_end - from->buf) * DIG_PER_DEC1 + + (i= ((from->frac - 1) % DIG_PER_DEC1 + 1))); + i= DIG_PER_DEC1 - i + 1; + } + else + { + stop= (buf_end - from->buf + 1) * DIG_PER_DEC1; + i= 1; + } + for (; *buf_end % powers10[i++] == 0; stop--); + *end_result= stop; /* index of position after last decimal digit (from 0) */ +} + + +/* + Left shift for alignment of data in buffer + + SYNOPSIS + do_mini_left_shift() + dec pointer to decimal number which have to be shifted + shift number of decimal digits on which it should be shifted + beg/end bounds of decimal digits (see digits_bounds()) + + NOTE + Result fitting in the buffer should be garanted. + 'shift' have to be from 1 to DIG_PER_DEC1-1 (inclusive) +*/ + +void do_mini_left_shift(decimal *dec, int shift, int beg, int last) +{ + dec1 *from= dec->buf + ROUND_UP(beg + 1) - 1; + dec1 *end= dec->buf + ROUND_UP(last) - 1; + int c_shift= DIG_PER_DEC1 - shift; + DBUG_ASSERT(from >= dec->buf); + DBUG_ASSERT(end < dec->buf + dec->len); + if (beg % DIG_PER_DEC1 < shift) + *(from - 1)= (*from) / powers10[c_shift]; + for(; from < end; from++) + *from= ((*from % powers10[c_shift]) * powers10[shift] + + (*(from + 1)) / powers10[c_shift]); + *from= (*from % powers10[c_shift]) * powers10[shift]; +} + + +/* + Right shift for alignment of data in buffer + + SYNOPSIS + do_mini_left_shift() + dec pointer to decimal number which have to be shifted + shift number of decimal digits on which it should be shifted + beg/end bounds of decimal digits (see digits_bounds()) + + NOTE + Result fitting in the buffer should be garanted. + 'shift' have to be from 1 to DIG_PER_DEC1-1 (inclusive) +*/ + +void do_mini_right_shift(decimal *dec, int shift, int beg, int last) +{ + dec1 *from= dec->buf + ROUND_UP(last) - 1; + dec1 *end= dec->buf + ROUND_UP(beg + 1) - 1; + int c_shift= DIG_PER_DEC1 - shift; + DBUG_ASSERT(from < dec->buf + dec->len); + DBUG_ASSERT(end >= dec->buf); + if (DIG_PER_DEC1 - ((last - 1) % DIG_PER_DEC1 + 1) < shift) + *(from + 1)= (*from % powers10[shift]) * powers10[c_shift]; + for(; from > end; from--) + *from= (*from / powers10[shift] + + (*(from - 1) % powers10[shift]) * powers10[c_shift]); + *from= *from / powers10[shift]; +} + + +/* + Shift of decimal digits in given number (with rounding if it need) + + SYNOPSIS + decimal_shift() + dec number to be shifted + shift number of decimal positions + shift > 0 means shift to left shift + shift < 0 meand right shift + NOTE + In fact it is multipling on 10^shift. + RETURN + E_DEC_OK OK + E_DEC_OVERFLOW operation lead to overflow, number is untoched + E_DEC_TRUNCATED number was rounded to fit into buffer +*/ + +int decimal_shift(decimal *dec, int shift) +{ + /* index of first non zero digit (all indexes from 0) */ + int beg; + /* index of position after last decimal digit */ + int end; + /* index of digit position just after point */ + int point= ROUND_UP(dec->intg) * DIG_PER_DEC1; + /* new point position */ + int new_point= point + shift; + /* number of digits in result */ + int digits_int, digits_frac; + /* length of result and new fraction in big digits*/ + int new_len, new_frac_len; + /* return code */ + int err= E_DEC_OK; + int new_front; + + if (shift == 0) + return E_DEC_OK; + + digits_bounds(dec, &beg, &end); + + if (beg == end) + { + decimal_make_zero(dec); + return E_DEC_OK; + } + + digits_int= new_point - beg; + set_if_bigger(digits_int, 0); + digits_frac= end - new_point; + set_if_bigger(digits_frac, 0); + + if ((new_len= ROUND_UP(digits_int) + (new_frac_len= ROUND_UP(digits_frac))) > + dec->len) + { + int lack= new_len - dec->len; + int diff; + + if (new_frac_len < lack) + return E_DEC_OVERFLOW; /* lack more then we have in fraction */ + + /* cat off fraction part to allow new number to fit in our buffer */ + err= E_DEC_TRUNCATED; + new_frac_len-= lack; + diff= digits_frac - (new_frac_len * DIG_PER_DEC1); + /* Make rounding method as parameter? */ + decimal_round(dec, dec, end - point - diff, HALF_UP); + end-= diff; + digits_frac= new_frac_len * DIG_PER_DEC1; + + if (end <= beg) + { + /* + we lost all digits (they will be shifted out of buffer), so we can + just return 0 + */ + decimal_make_zero(dec); + return E_DEC_TRUNCATED; + } + } + + if (shift % DIG_PER_DEC1) + { + int l_mini_shift, r_mini_shift, mini_shift; + int do_left; + /* + Calculate left/right shift to align decimal digits inside our bug + digits correctly + */ + if (shift > 0) + { + l_mini_shift= shift % DIG_PER_DEC1; + r_mini_shift= DIG_PER_DEC1 - l_mini_shift; + /* + It is left shift so prefer left shift, but if we have not place from + left, we have to have it from right, because we checked length of + result + */ + do_left= l_mini_shift <= beg; + DBUG_ASSERT(do_left || (dec->len * DIG_PER_DEC1 - end) >= r_mini_shift); + } + else + { + r_mini_shift= (-shift) % DIG_PER_DEC1; + l_mini_shift= DIG_PER_DEC1 - r_mini_shift; + /* see comment above */ + do_left= !((dec->len * DIG_PER_DEC1 - end) >= r_mini_shift); + DBUG_ASSERT(!do_left || l_mini_shift <= beg); + } + if (do_left) + { + do_mini_left_shift(dec, l_mini_shift, beg, end); + mini_shift=- l_mini_shift; + } + else + { + do_mini_right_shift(dec, r_mini_shift, beg, end); + mini_shift= r_mini_shift; + } + new_point+= mini_shift; + /* + If number is shifted and correctly aligned in buffer we can + finish + */ + if (!(shift+= mini_shift) && (new_point - digits_int) < DIG_PER_DEC1) + { + dec->intg= digits_int; + dec->frac= digits_frac; + return err; /* already shifted as it should be */ + } + beg+= mini_shift; + end+= mini_shift; + } + + /* if new 'decimal front' is in first digit, we do not need move digits */ + if ((new_front= (new_point - digits_int)) >= DIG_PER_DEC1 || + new_front < 0) + { + /* need to move digits */ + int d_shift; + if (new_front > 0) + { + /* move left */ + d_shift= new_front / DIG_PER_DEC1; + dec1 *to= dec->buf + (ROUND_UP(beg + 1) - 1 - d_shift); + dec1 *barier= dec->buf + (ROUND_UP(end) - 1 - d_shift); + DBUG_ASSERT(to >= dec->buf); + DBUG_ASSERT(barier + d_shift < dec->buf + dec->len); + for(; to <= barier; to++) + *to= *(to + d_shift); + for(barier+= d_shift; to <= barier; to++) + *to= 0; + d_shift= -d_shift; + } + else + { + /* move right */ + d_shift= (1 - new_front) / DIG_PER_DEC1; + dec1 *to= dec->buf + ROUND_UP(end) - 1 + d_shift; + dec1 *barier= dec->buf + ROUND_UP(beg + 1) - 1 + d_shift; + DBUG_ASSERT(to < dec->buf + dec->len); + DBUG_ASSERT(barier - d_shift >= dec->buf); + for(; to >= barier; to--) + *to= *(to - d_shift); + for(barier-= d_shift; to >= barier; to--) + *to= 0; + } + d_shift*= DIG_PER_DEC1; + beg+= d_shift; + end+= d_shift; + new_point+= d_shift; + } + + /* + If there are gaps then fill ren with 0. + + Only one of following 'for' loops will work becouse beg <= end + */ + beg= ROUND_UP(beg + 1) - 1; + end= ROUND_UP(end) - 1; + DBUG_ASSERT(new_point >= 0); + new_point= ROUND_UP(new_point) - 1; + for(; new_point > end; new_point--) + dec->buf[new_point]= 0; + for(; new_point < beg; new_point++) + dec->buf[new_point]= 0; + dec->intg= digits_int; + dec->frac= digits_frac; + return err; +} + + /* Convert string to decimal @@ -309,7 +760,7 @@ int decimal2string(decimal *from, char *to, int *to_len) static int str2dec(char *from, decimal *to, char **end, my_bool fixed) { - char *s=from, *s1; + char *s=from, *s1, *endp; int i, intg, frac, error, intg1, frac1; dec1 x,*buf; LINT_INIT(error); @@ -328,15 +779,19 @@ static int str2dec(char *from, decimal *to, char **end, my_bool fixed) intg=s-s1; if (*s=='.') { - char *s2=s+1; - while (my_isdigit(&my_charset_latin1, *s2)) - s2++; - frac=s2-s-1; + endp= s+1; + while (my_isdigit(&my_charset_latin1, *endp)) + endp++; + frac= endp - s - 1; } else - frac=0; + { + frac= 0; + endp= s; + } + if (end) - *end=s1+intg+frac+test(frac); + *end= endp; if (frac+intg == 0) return E_DEC_BAD_NUM; @@ -405,10 +860,26 @@ static int str2dec(char *from, decimal *to, char **end, my_bool fixed) } if (i) *buf=x*powers10[DIG_PER_DEC1-i]; + if (*endp == 'e' || *endp == 'E') + { + long exp= strtol(endp + 1, &endp, 10); + + if (end) + *end= endp; + + if (exp > INT_MAX/2) + return E_DEC_OVERFLOW; + if (exp < INT_MIN/2 && error != E_DEC_OVERFLOW) + return E_DEC_TRUNCATED; + + if(error != E_DEC_OVERFLOW) + error= decimal_shift(to, exp); + } return error; } + int string2decimal(char *from, decimal *to, char **end) { return str2dec(from, to, end, 0); @@ -590,7 +1061,7 @@ int decimal2bin(decimal *from, char *to, int precision, int frac) { dec1 mask=from->sign ? -1 : 0, *buf1=from->buf, *stop1; int error=E_DEC_OK, intg=precision-frac, - isize1, intg1, intg1x, from_intg=from->intg, + isize1, intg1, intg1x, from_intg, intg0=intg/DIG_PER_DEC1, frac0=frac/DIG_PER_DEC1, intg0x=intg-intg0*DIG_PER_DEC1, @@ -600,22 +1071,9 @@ int decimal2bin(decimal *from, char *to, int precision, int frac) isize0=intg0*sizeof(dec1)+dig2bytes[intg0x], fsize0=frac0*sizeof(dec1)+dig2bytes[frac0x], fsize1=frac1*sizeof(dec1)+dig2bytes[frac1x]; + char *orig_to= to; - /* removing leading zeroes */ - intg1=((from_intg-1) % DIG_PER_DEC1)+1; - while (from_intg > 0 && *buf1 == 0) - { - from_intg-=intg1; - intg1=DIG_PER_DEC1; - buf1++; - } - if (from_intg > 0) - { - for (intg1=(from_intg-1) % DIG_PER_DEC1; *buf1 < powers10[intg1--]; from_intg--) ; - DBUG_ASSERT(from_intg > 0); - } - else - from_intg=0; + buf1= remove_leading_zeroes(from, &from_intg); if (unlikely(from_intg+fsize1==0)) { @@ -703,6 +1161,7 @@ int decimal2bin(decimal *from, char *to, int precision, int frac) while (fsize0-- > fsize1) *to++=(uchar)mask; } + orig_to[0]^= 0x80; return error; } @@ -729,10 +1188,16 @@ int bin2decimal(char *from, decimal *to, int precision, int scale) intg0=intg/DIG_PER_DEC1, frac0=scale/DIG_PER_DEC1, intg0x=intg-intg0*DIG_PER_DEC1, frac0x=scale-frac0*DIG_PER_DEC1, intg1=intg0+(intg0x>0), frac1=frac0+(frac0x>0); - dec1 *buf=to->buf, mask=(*from <0) ? -1 : 0; + dec1 *buf=to->buf, mask=(*from <0) ? 0 : -1; char *stop; + char *d_copy; + int bin_size= decimal_bin_size(precision, scale); sanity(to); + d_copy= (char *)my_alloca(bin_size); + memcpy(d_copy, from, bin_size); + d_copy[0]^= 0x80; + from= d_copy; FIX_INTG_FRAC_ERROR(to->len, intg1, frac1, error); if (unlikely(error)) @@ -804,6 +1269,7 @@ int bin2decimal(char *from, decimal *to, int precision, int scale) *buf=(x ^ mask) * powers10[DIG_PER_DEC1 - frac0x]; buf++; } + my_afree(d_copy); return error; } @@ -861,7 +1327,9 @@ int decimal_round(decimal *from, decimal *to, int scale, decimal_round_mode mode { int frac0=scale>0 ? ROUND_UP(scale) : scale/DIG_PER_DEC1, frac1=ROUND_UP(from->frac), round_digit, - intg0=ROUND_UP(from->intg), error=E_DEC_OK, len=to->len; + intg0=ROUND_UP(from->intg), error=E_DEC_OK, len=to->len, + intg1=ROUND_UP(from->intg + + (((intg0 + frac0)>0) && (from->buf[0] == DIG_MAX))); dec1 *buf0=from->buf, *buf1=to->buf, x, y, carry=0; sanity(to); @@ -888,12 +1356,17 @@ int decimal_round(decimal *from, decimal *to, int scale, decimal_round_mode mode return E_DEC_OK; } - if (to != from) + if (to != from || intg1>intg0) { - dec1 *end=buf0+intg0+min(frac1, frac0); - while (buf0 < end) - *buf1++ = *buf0++; - buf0=from->buf; + dec1 *p0= buf0+intg0+max(frac1, frac0); + dec1 *p1= buf1+intg1+max(frac1, frac0); + + to->buf[0]= 0; + while (buf0 < p0) + *(--p1) = *(--p0); + + intg0= intg1; + buf0=to->buf; buf1=to->buf; to->sign=from->sign; to->intg=min(intg0, len)*DIG_PER_DEC1; @@ -1446,9 +1919,9 @@ static int do_div_mod(decimal *from1, decimal *from2, while (dintg++ < 0) *buf0++=0; - len1=(i=ROUND_UP(prec1))+ROUND_UP(2*frac2+scale_incr+1); + len1=(i=ROUND_UP(prec1))+ROUND_UP(2*frac2+scale_incr+1) + 1; set_if_bigger(len1, 3); - if (!(tmp1=my_alloca(len1*sizeof(dec1)))) + if (!(tmp1=(dec1 *)my_alloca(len1*sizeof(dec1)))) return E_DEC_OOM; memcpy(tmp1, buf1, i*sizeof(dec1)); bzero(tmp1+i, (len1-i)*sizeof(dec1)); @@ -1668,7 +2141,7 @@ int decimal_mod(decimal *from1, decimal *from2, decimal *to) #ifdef MAIN -int full=0; +int full= 0; decimal a, b, c; char buf1[100], buf2[100], buf3[100]; @@ -1681,14 +2154,26 @@ void dump_decimal(decimal *d) printf("%09d} */ ", d->buf[i]); } -void print_decimal(decimal *d, char *orig) + +void check_result_code(int actual, int want) +{ + if (actual != want) + { + printf("\n^^^^^^^^^^^^^ mast return %d\n", want); + exit(1); + } +} + + +void print_decimal(decimal *d, char *orig, int actual, int want) { char s[100]; int slen=sizeof(s); if (full) dump_decimal(d); - decimal2string(d, s, &slen); + decimal2string(d, s, &slen, 0, 0, 0); printf("'%s'", s); + check_result_code(actual, want); if (orig && strcmp(orig, s)) { printf("\n^^^^^^^^^^^^^ must've been '%s'\n", orig); @@ -1705,43 +2190,45 @@ void test_d2s() printf("==== decimal2string ====\n"); a.buf[0]=12345; a.intg=5; a.frac=0; a.sign=0; slen=sizeof(s); - res=decimal2string(&a, s, &slen); + res=decimal2string(&a, s, &slen, 0, 0, 0); dump_decimal(&a); printf(" --> res=%d str='%s' len=%d\n", res, s, slen); a.buf[1]=987000000; a.frac=3; slen=sizeof(s); - res=decimal2string(&a, s, &slen); + res=decimal2string(&a, s, &slen, 0, 0, 0); dump_decimal(&a); printf(" --> res=%d str='%s' len=%d\n", res, s, slen); a.sign=1; slen=sizeof(s); - res=decimal2string(&a, s, &slen); + res=decimal2string(&a, s, &slen, 0, 0, 0); dump_decimal(&a); printf(" --> res=%d str='%s' len=%d\n", res, s, slen); slen=8; - res=decimal2string(&a, s, &slen); + res=decimal2string(&a, s, &slen, 0, 0, 0); dump_decimal(&a); printf(" --> res=%d str='%s' len=%d\n", res, s, slen); slen=5; - res=decimal2string(&a, s, &slen); + res=decimal2string(&a, s, &slen, 0, 0, 0); dump_decimal(&a); printf(" --> res=%d str='%s' len=%d\n", res, s, slen); a.buf[0]=987000000; a.frac=3; a.intg=0; slen=sizeof(s); - res=decimal2string(&a, s, &slen); + res=decimal2string(&a, s, &slen, 0, 0, 0); dump_decimal(&a); printf(" --> res=%d str='%s' len=%d\n", res, s, slen); } -void test_s2d(char *s, char *orig) +void test_s2d(char *s, char *orig, int ex) { char s1[100]; + int res; sprintf(s1, "'%s'", s); - printf("len=%2d %-30s => res=%d ", a.len, s1, string2decimal(s, &a, 0)); - print_decimal(&a, orig); + printf("len=%2d %-30s => res=%d ", a.len, s1, + (res= string2decimal(s, &a, 0))); + print_decimal(&a, orig, res, ex); printf("\n"); } -void test_d2f(char *s) +void test_d2f(char *s, int ex) { char s1[100]; double x; @@ -1752,12 +2239,12 @@ void test_d2f(char *s) res=decimal2double(&a, &x); if (full) dump_decimal(&a); printf("%-40s => res=%d %.*g\n", s1, res, a.intg+a.frac, x); + check_result_code(res, ex); } -void test_d2b2d(char *str, int p, int s, char *orig) +void test_d2b2d(char *str, int p, int s, char *orig, int ex) { char s1[100], buf[100]; - double x; int res, i, size=decimal_bin_size(p, s); sprintf(s1, "'%s'", str); @@ -1772,20 +2259,20 @@ void test_d2b2d(char *str, int p, int s, char *orig) } res=bin2decimal(buf, &a, p, s); printf(" => res=%d ", res); - print_decimal(&a, orig); + print_decimal(&a, orig, res, ex); printf("\n"); } -void test_f2d(double from) +void test_f2d(double from, int ex) { int res; res=double2decimal(from, &a); printf("%-40.*f => res=%d ", DBL_DIG-2, from, res); - print_decimal(&a, 0); + print_decimal(&a, 0, res, ex); printf("\n"); } -void test_ull2d(ulonglong from, char *orig) +void test_ull2d(ulonglong from, char *orig, int ex) { char s[100]; int res; @@ -1793,11 +2280,11 @@ void test_ull2d(ulonglong from, char *orig) res=ulonglong2decimal(from, &a); longlong10_to_str(from,s,10); printf("%-40s => res=%d ", s, res); - print_decimal(&a, orig); + print_decimal(&a, orig, res, ex); printf("\n"); } -void test_ll2d(longlong from, char *orig) +void test_ll2d(longlong from, char *orig, int ex) { char s[100]; int res; @@ -1805,11 +2292,11 @@ void test_ll2d(longlong from, char *orig) res=longlong2decimal(from, &a); longlong10_to_str(from,s,-10); printf("%-40s => res=%d ", s, res); - print_decimal(&a, orig); + print_decimal(&a, orig, res, ex); printf("\n"); } -void test_d2ull(char *s, char *orig) +void test_d2ull(char *s, char *orig, int ex) { char s1[100]; ulonglong x; @@ -1820,6 +2307,7 @@ void test_d2ull(char *s, char *orig) if (full) dump_decimal(&a); longlong10_to_str(x,s1,10); printf("%-40s => res=%d %s\n", s, res, s1); + check_result_code(res, ex); if (orig && strcmp(orig, s1)) { printf("\n^^^^^^^^^^^^^ must've been '%s'\n", orig); @@ -1827,7 +2315,7 @@ void test_d2ull(char *s, char *orig) } } -void test_d2ll(char *s, char *orig) +void test_d2ll(char *s, char *orig, int ex) { char s1[100]; longlong x; @@ -1838,6 +2326,7 @@ void test_d2ll(char *s, char *orig) if (full) dump_decimal(&a); longlong10_to_str(x,s1,-10); printf("%-40s => res=%d %s\n", s, res, s1); + check_result_code(res, ex); if (orig && strcmp(orig, s1)) { printf("\n^^^^^^^^^^^^^ must've been '%s'\n", orig); @@ -1845,7 +2334,7 @@ void test_d2ll(char *s, char *orig) } } -void test_da(char *s1, char *s2, char *orig) +void test_da(char *s1, char *s2, char *orig, int ex) { char s[100]; int res; @@ -1854,11 +2343,11 @@ void test_da(char *s1, char *s2, char *orig) string2decimal(s2, &b, 0); res=decimal_add(&a, &b, &c); printf("%-40s => res=%d ", s, res); - print_decimal(&c, orig); + print_decimal(&c, orig, res, ex); printf("\n"); } -void test_ds(char *s1, char *s2, char *orig) +void test_ds(char *s1, char *s2, char *orig, int ex) { char s[100]; int res; @@ -1867,7 +2356,7 @@ void test_ds(char *s1, char *s2, char *orig) string2decimal(s2, &b, 0); res=decimal_sub(&a, &b, &c); printf("%-40s => res=%d ", s, res); - print_decimal(&c, orig); + print_decimal(&c, orig, res, ex); printf("\n"); } @@ -1887,7 +2376,7 @@ void test_dc(char *s1, char *s2, int orig) } } -void test_dm(char *s1, char *s2, char *orig) +void test_dm(char *s1, char *s2, char *orig, int ex) { char s[100]; int res; @@ -1896,11 +2385,11 @@ void test_dm(char *s1, char *s2, char *orig) string2decimal(s2, &b, 0); res=decimal_mul(&a, &b, &c); printf("%-40s => res=%d ", s, res); - print_decimal(&c, orig); + print_decimal(&c, orig, res, ex); printf("\n"); } -void test_dv(char *s1, char *s2, char *orig) +void test_dv(char *s1, char *s2, char *orig, int ex) { char s[100]; int res; @@ -1909,14 +2398,15 @@ void test_dv(char *s1, char *s2, char *orig) string2decimal(s2, &b, 0); res=decimal_div(&a, &b, &c, 5); printf("%-40s => res=%d ", s, res); + check_result_code(res, ex); if (res == E_DEC_DIV_ZERO) printf("E_DEC_DIV_ZERO"); else - print_decimal(&c, orig); + print_decimal(&c, orig, res, ex); printf("\n"); } -void test_md(char *s1, char *s2, char *orig) +void test_md(char *s1, char *s2, char *orig, int ex) { char s[100]; int res; @@ -1925,16 +2415,17 @@ void test_md(char *s1, char *s2, char *orig) string2decimal(s2, &b, 0); res=decimal_mod(&a, &b, &c); printf("%-40s => res=%d ", s, res); + check_result_code(res, ex); if (res == E_DEC_DIV_ZERO) printf("E_DEC_DIV_ZERO"); else - print_decimal(&c, orig); + print_decimal(&c, orig, res, ex); printf("\n"); } char *round_mode[]={"TRUNCATE", "HALF_EVEN", "HALF_UP", "CEILING", "FLOOR"}; -void test_ro(char *s1, int n, decimal_round_mode mode, char *orig) +void test_ro(char *s1, int n, decimal_round_mode mode, char *orig, int ex) { char s[100]; int res; @@ -1942,11 +2433,69 @@ void test_ro(char *s1, int n, decimal_round_mode mode, char *orig) string2decimal(s1, &a, 0); res=decimal_round(&a, &b, n, mode); printf("%-40s => res=%d ", s, res); - print_decimal(&b, orig); + print_decimal(&b, orig, res, ex); + printf("\n"); +} + + +void test_mx(int precision, int frac, char *orig) +{ + char s[100]; + sprintf(s, "%d, %d", precision, frac); + max_decimal(precision, frac, &a); + printf("%-40s => ", s); + print_decimal(&a, orig, 0, 0); + printf("\n"); +} + + +void test_pr(char *s1, int prec, int dec, char filler, char *orig, int ex) +{ + char s[100]; + char s2[100]; + int slen= sizeof(s2); + int res; + + sprintf(s, "'%s', %d, %d, '%c'", s1, prec, dec, filler); + string2decimal(s1, &a, 0); + res= decimal2string(&a, s2, &slen, prec, dec, filler); + printf("%-40s => res=%d '%s'", s, res, s2); + check_result_code(res, ex); + if (orig && strcmp(orig, s2)) + { + printf("\n^^^^^^^^^^^^^ must've been '%s'\n", orig); + exit(1); + } + printf("\n"); +} + + +void test_sh(char *s1, int shift, char *orig, int ex) +{ + char s[100]; + int res; + sprintf(s, "'%s' %s %d", s1, ((shift < 0) ? ">>" : "<<"), abs(shift)); + string2decimal(s1, &a, 0); + res= decimal_shift(&a, shift); + printf("%-40s => res=%d ", s, res); + print_decimal(&a, orig, res, ex); printf("\n"); } -main() + +void test_fr(char *s1, char *orig) +{ + char s[100]; + sprintf(s, "'%s'", s1); + printf("%-40s => ", s); + string2decimal(s1, &a, 0); + decimal_optimize_fraction(&a); + print_decimal(&a, orig, 0, 0); + printf("\n"); +} + + +int main() { a.buf=(void*)buf1; a.len=sizeof(buf1)/sizeof(dec1); @@ -1959,142 +2508,145 @@ main() test_d2s(); printf("==== string2decimal ====\n"); - test_s2d("12345", "12345"); - test_s2d("12345.", "12345"); - test_s2d("123.45", "123.45"); - test_s2d("-123.45", "-123.45"); - test_s2d(".00012345000098765", ".00012345000098765"); - test_s2d(".12345000098765", ".12345000098765"); - test_s2d("-.000000012345000098765", "-.000000012345000098765"); - test_s2d("1234500009876.5", "1234500009876.5"); + test_s2d("12345", "12345", 0); + test_s2d("12345.", "12345", 0); + test_s2d("123.45", "123.45", 0); + test_s2d("-123.45", "-123.45", 0); + test_s2d(".00012345000098765", "0.00012345000098765", 0); + test_s2d(".12345000098765", "0.12345000098765", 0); + test_s2d("-.000000012345000098765", "-0.000000012345000098765", 0); + test_s2d("1234500009876.5", "1234500009876.5", 0); a.len=1; - test_s2d("123450000098765", "98765"); - test_s2d("123450.000098765", "123450"); + test_s2d("123450000098765", "98765", 2); + test_s2d("123450.000098765", "123450", 1); a.len=sizeof(buf1)/sizeof(dec1); + test_s2d("123E5", "12300000", 0); + test_s2d("123E-2", "1.23", 0); printf("==== decimal2double ====\n"); - test_d2f("12345"); - test_d2f("123.45"); - test_d2f("-123.45"); - test_d2f(".00012345000098765"); - test_d2f("1234500009876.5"); + test_d2f("12345", 0); + test_d2f("123.45", 0); + test_d2f("-123.45", 0); + test_d2f("0.00012345000098765", 0); + test_d2f("1234500009876.5", 0); printf("==== double2decimal ====\n"); - test_f2d(12345); - test_f2d(1.0/3); - test_f2d(-123.45); - test_f2d(0.00012345000098765); - test_f2d(1234500009876.5); + test_f2d(12345, 0); + test_f2d(1.0/3, 0); + test_f2d(-123.45, 0); + test_f2d(0.00012345000098765, 0); + test_f2d(1234500009876.5, 0); printf("==== ulonglong2decimal ====\n"); - test_ull2d(ULL(12345), "12345"); - test_ull2d(ULL(0), "0"); - test_ull2d(ULL(18446744073709551615), "18446744073709551615"); + test_ull2d(ULL(12345), "12345", 0); + test_ull2d(ULL(0), "0", 0); + test_ull2d(ULL(18446744073709551615), "18446744073709551615", 0); printf("==== decimal2ulonglong ====\n"); - test_d2ull("12345", "12345"); - test_d2ull("0", "0"); - test_d2ull("18446744073709551615", "18446744073709551615"); - test_d2ull("18446744073709551616", "18446744073"); - test_d2ull("-1", "0"); - test_d2ull("1.23", "1"); - test_d2ull("9999999999999999999999999.000", "9999999999999999"); + test_d2ull("12345", "12345", 0); + test_d2ull("0", "0", 0); + test_d2ull("18446744073709551615", "18446744073709551615", 0); + test_d2ull("18446744073709551616", "18446744073", 2); + test_d2ull("-1", "0", 2); + test_d2ull("1.23", "1", 1); + test_d2ull("9999999999999999999999999.000", "9999999999999999", 2); printf("==== longlong2decimal ====\n"); - test_ll2d(LL(-12345), "-12345"); - test_ll2d(LL(-1), "-1"); - test_ll2d(LL(-9223372036854775807), "-9223372036854775807"); - test_ll2d(ULL(9223372036854775808), "-9223372036854775808"); + test_ll2d(LL(-12345), "-12345", 0); + test_ll2d(LL(-1), "-1", 0); + test_ll2d(LL(-9223372036854775807), "-9223372036854775807", 0); + test_ll2d(ULL(9223372036854775808), "-9223372036854775808", 0); printf("==== decimal2longlong ====\n"); - test_d2ll("18446744073709551615", "18446744073"); - test_d2ll("-1", "-1"); - test_d2ll("-1.23", "-1"); - test_d2ll("-9223372036854775807", "-9223372036854775807"); - test_d2ll("-9223372036854775808", "-9223372036854775808"); - test_d2ll("9223372036854775808", "9223372036854775807"); + test_d2ll("18446744073709551615", "18446744073", 2); + test_d2ll("-1", "-1", 0); + test_d2ll("-1.23", "-1", 1); + test_d2ll("-9223372036854775807", "-9223372036854775807", 0); + test_d2ll("-9223372036854775808", "-9223372036854775808", 0); + test_d2ll("9223372036854775808", "9223372036854775807", 2); printf("==== do_add ====\n"); - test_da(".00012345000098765" ,"123.45", "123.45012345000098765"); - test_da(".1" ,".45", ".55"); - test_da("1234500009876.5" ,".00012345000098765", "1234500009876.50012345000098765"); - test_da("9999909999999.5" ,".555", "9999910000000.055"); - test_da("99999999" ,"1", "100000000"); - test_da("989999999" ,"1", "990000000"); - test_da("999999999" ,"1", "1000000000"); - test_da("12345" ,"123.45", "12468.45"); - test_da("-12345" ,"-123.45", "-12468.45"); - test_ds("-12345" ,"123.45", "-12468.45"); - test_ds("12345" ,"-123.45", "12468.45"); + test_da(".00012345000098765" ,"123.45", "123.45012345000098765", 0); + test_da(".1" ,".45", "0.55", 0); + test_da("1234500009876.5" ,".00012345000098765", "1234500009876.50012345000098765", 0); + test_da("9999909999999.5" ,".555", "9999910000000.055", 0); + test_da("99999999" ,"1", "100000000", 0); + test_da("989999999" ,"1", "990000000", 0); + test_da("999999999" ,"1", "1000000000", 0); + test_da("12345" ,"123.45", "12468.45", 0); + test_da("-12345" ,"-123.45", "-12468.45", 0); + test_ds("-12345" ,"123.45", "-12468.45", 0); + test_ds("12345" ,"-123.45", "12468.45", 0); printf("==== do_sub ====\n"); - test_ds(".00012345000098765", "123.45","-123.44987654999901235"); - test_ds("1234500009876.5", ".00012345000098765","1234500009876.49987654999901235"); - test_ds("9999900000000.5", ".555","9999899999999.945"); - test_ds("1111.5551", "1111.555",".0001"); - test_ds(".555", ".555","0"); - test_ds("10000000", "1","9999999"); - test_ds("1000001000", ".1","1000000999.9"); - test_ds("1000000000", ".1","999999999.9"); - test_ds("12345", "123.45","12221.55"); - test_ds("-12345", "-123.45","-12221.55"); - test_da("-12345", "123.45","-12221.55"); - test_da("12345", "-123.45","12221.55"); - test_ds("123.45", "12345","-12221.55"); - test_ds("-123.45", "-12345","12221.55"); - test_da("123.45", "-12345","-12221.55"); - test_da("-123.45", "12345","12221.55"); - test_da("5", "-6.0","-1.0"); + test_ds(".00012345000098765", "123.45","-123.44987654999901235", 0); + test_ds("1234500009876.5", ".00012345000098765","1234500009876.49987654999901235", 0); + test_ds("9999900000000.5", ".555","9999899999999.945", 0); + test_ds("1111.5551", "1111.555","0.0001", 0); + test_ds(".555", ".555","0", 0); + test_ds("10000000", "1","9999999", 0); + test_ds("1000001000", ".1","1000000999.9", 0); + test_ds("1000000000", ".1","999999999.9", 0); + test_ds("12345", "123.45","12221.55", 0); + test_ds("-12345", "-123.45","-12221.55", 0); + test_da("-12345", "123.45","-12221.55", 0); + test_da("12345", "-123.45","12221.55", 0); + test_ds("123.45", "12345","-12221.55", 0); + test_ds("-123.45", "-12345","12221.55", 0); + test_da("123.45", "-12345","-12221.55", 0); + test_da("-123.45", "12345","12221.55", 0); + test_da("5", "-6.0","-1.0", 0); printf("==== decimal_mul ====\n"); - test_dm("12", "10","120"); - test_dm("-123.456", "98765.4321","-12193185.1853376"); - test_dm("-123456000000", "98765432100000","-12193185185337600000000000"); - test_dm("123456", "987654321","121931851853376"); - test_dm("123456", "9876543210","1219318518533760"); - test_dm("123", "0.01","1.23"); - test_dm("123", "0","0"); + test_dm("12", "10","120", 0); + test_dm("-123.456", "98765.4321","-12193185.1853376", 0); + test_dm("-123456000000", "98765432100000","-12193185185337600000000000", 0); + test_dm("123456", "987654321","121931851853376", 0); + test_dm("123456", "9876543210","1219318518533760", 0); + test_dm("123", "0.01","1.23", 0); + test_dm("123", "0","0", 0); printf("==== decimal_div ====\n"); - test_dv("120", "10","12.000000000"); - test_dv("123", "0.01","12300.000000000"); - test_dv("120", "100000000000.00000",".000000001200000000"); - test_dv("123", "0",""); - test_dv("-12193185.1853376", "98765.4321","-123.456000000000000000"); - test_dv("121931851853376", "987654321","123456.000000000"); - test_dv("0", "987","0"); - test_dv("1", "3",".333333333"); - test_dv("1.000000000000", "3",".333333333333333333"); - test_dv("1", "1","1.000000000"); - test_dv("0.0123456789012345678912345", "9999999999",".000000000001234567890246913578148141"); + test_dv("120", "10","12.000000000", 0); + test_dv("123", "0.01","12300.000000000", 0); + test_dv("120", "100000000000.00000","0.000000001200000000", 0); + test_dv("123", "0","", 4); + test_dv("-12193185.1853376", "98765.4321","-123.456000000000000000", 0); + test_dv("121931851853376", "987654321","123456.000000000", 0); + test_dv("0", "987","0", 0); + test_dv("1", "3","0.333333333", 0); + test_dv("1.000000000000", "3","0.333333333333333333", 0); + test_dv("1", "1","1.000000000", 0); + test_dv("0.0123456789012345678912345", "9999999999","0.000000000001234567890246913578148141", 0); printf("==== decimal_mod ====\n"); - test_md("234","10","4"); - test_md("234.567","10.555","2.357"); - test_md("-234.567","10.555","-2.357"); - test_md("234.567","-10.555","2.357"); + test_md("234","10","4", 0); + test_md("234.567","10.555","2.357", 0); + test_md("-234.567","10.555","-2.357", 0); + test_md("234.567","-10.555","2.357", 0); if (full) { c.buf[1]=0x3ABECA; - test_md("99999999999999999999999999999999999999","3","0"); + test_md("99999999999999999999999999999999999999","3","0", 0); printf("%X\n", c.buf[1]); } printf("==== decimal2bin/bin2decimal ====\n"); - test_d2b2d("-10.55", 4, 2,"-10.55"); - test_d2b2d("0.0123456789012345678912345", 30, 25,".0123456789012345678912345"); - test_d2b2d("12345", 5, 0,"12345"); - test_d2b2d("12345", 10, 3,"12345.000"); - test_d2b2d("123.45", 10, 3,"123.450"); - test_d2b2d("-123.45", 20, 10,"-123.4500000000"); - test_d2b2d(".00012345000098765", 15, 14,".00012345000098"); - test_d2b2d(".00012345000098765", 22, 20,".00012345000098765000"); - test_d2b2d(".12345000098765", 30, 20,".12345000098765000000"); - test_d2b2d("-.000000012345000098765", 30, 20,"-.00000001234500009876"); - test_d2b2d("1234500009876.5", 30, 5,"1234500009876.50000"); - test_d2b2d("111111111.11", 10, 2,"11111111.11"); - full=1; - test_d2b2d("123.4", 10, 2, "123.40"); + test_d2b2d("-10.55", 4, 2,"-10.55", 0); + test_d2b2d("0.0123456789012345678912345", 30, 25,"0.0123456789012345678912345", 0); + test_d2b2d("12345", 5, 0,"12345", 0); + test_d2b2d("12345", 10, 3,"12345.000", 0); + test_d2b2d("123.45", 10, 3,"123.450", 0); + test_d2b2d("-123.45", 20, 10,"-123.4500000000", 0); + test_d2b2d(".00012345000098765", 15, 14,"0.00012345000098", 0); + test_d2b2d(".00012345000098765", 22, 20,"0.00012345000098765000", 0); + test_d2b2d(".12345000098765", 30, 20,"0.12345000098765000000", 0); + test_d2b2d("-.000000012345000098765", 30, 20,"-0.00000001234500009876", 0); + test_d2b2d("1234500009876.5", 30, 5,"1234500009876.50000", 0); + test_d2b2d("111111111.11", 10, 2,"11111111.11", 0); + test_d2b2d("000000000.01", 7, 3,"0.010", 0); + test_d2b2d("123.4", 10, 2, "123.40", 0); + printf("==== decimal_cmp ====\n"); test_dc("12","13",-1); @@ -2107,45 +2659,174 @@ main() test_dc("4","4",0); printf("==== decimal_round ====\n"); - test_ro("5678.123451",-4,TRUNCATE,"0"); - test_ro("5678.123451",-3,TRUNCATE,"5000"); - test_ro("5678.123451",-2,TRUNCATE,"5600"); - test_ro("5678.123451",-1,TRUNCATE,"5670"); - test_ro("5678.123451",0,TRUNCATE,"5678"); - test_ro("5678.123451",1,TRUNCATE,"5678.1"); - test_ro("5678.123451",2,TRUNCATE,"5678.12"); - test_ro("5678.123451",3,TRUNCATE,"5678.123"); - test_ro("5678.123451",4,TRUNCATE,"5678.1234"); - test_ro("5678.123451",5,TRUNCATE,"5678.12345"); - test_ro("5678.123451",6,TRUNCATE,"5678.123451"); - test_ro("-5678.123451",-4,TRUNCATE,"0"); + test_ro("5678.123451",-4,TRUNCATE,"0", 0); + test_ro("5678.123451",-3,TRUNCATE,"5000", 0); + test_ro("5678.123451",-2,TRUNCATE,"5600", 0); + test_ro("5678.123451",-1,TRUNCATE,"5670", 0); + test_ro("5678.123451",0,TRUNCATE,"5678", 0); + test_ro("5678.123451",1,TRUNCATE,"5678.1", 0); + test_ro("5678.123451",2,TRUNCATE,"5678.12", 0); + test_ro("5678.123451",3,TRUNCATE,"5678.123", 0); + test_ro("5678.123451",4,TRUNCATE,"5678.1234", 0); + test_ro("5678.123451",5,TRUNCATE,"5678.12345", 0); + test_ro("5678.123451",6,TRUNCATE,"5678.123451", 0); + test_ro("-5678.123451",-4,TRUNCATE,"0", 0); memset(buf2, 33, sizeof(buf2)); - test_ro("99999999999999999999999999999999999999",-31,TRUNCATE,"99999990000000000000000000000000000000"); - test_ro("15.1",0,HALF_UP,"15"); - test_ro("15.5",0,HALF_UP,"16"); - test_ro("15.9",0,HALF_UP,"16"); - test_ro("-15.1",0,HALF_UP,"-15"); - test_ro("-15.5",0,HALF_UP,"-16"); - test_ro("-15.9",0,HALF_UP,"-16"); - test_ro("15.1",1,HALF_UP,"15.1"); - test_ro("-15.1",1,HALF_UP,"-15.1"); - test_ro("15.17",1,HALF_UP,"15.2"); - test_ro("15.4",-1,HALF_UP,"20"); - test_ro("-15.4",-1,HALF_UP,"-20"); - test_ro("5.4",-1,HALF_UP,"10"); - test_ro(".999", 0, HALF_UP, "1"); + test_ro("99999999999999999999999999999999999999",-31,TRUNCATE,"99999990000000000000000000000000000000", 0); + test_ro("15.1",0,HALF_UP,"15", 0); + test_ro("15.5",0,HALF_UP,"16", 0); + test_ro("15.9",0,HALF_UP,"16", 0); + test_ro("-15.1",0,HALF_UP,"-15", 0); + test_ro("-15.5",0,HALF_UP,"-16", 0); + test_ro("-15.9",0,HALF_UP,"-16", 0); + test_ro("15.1",1,HALF_UP,"15.1", 0); + test_ro("-15.1",1,HALF_UP,"-15.1", 0); + test_ro("15.17",1,HALF_UP,"15.2", 0); + test_ro("15.4",-1,HALF_UP,"20", 0); + test_ro("-15.4",-1,HALF_UP,"-20", 0); + test_ro("5.4",-1,HALF_UP,"10", 0); + test_ro(".999", 0, HALF_UP, "1", 0); memset(buf2, 33, sizeof(buf2)); - test_ro("999999999", -9, HALF_UP, "1000000000"); - test_ro("15.1",0,HALF_EVEN,"15"); - test_ro("15.5",0,HALF_EVEN,"16"); - test_ro("14.5",0,HALF_EVEN,"14"); - test_ro("15.9",0,HALF_EVEN,"16"); - test_ro("15.1",0,CEILING,"16"); - test_ro("-15.1",0,CEILING,"-15"); - test_ro("15.1",0,FLOOR,"15"); - test_ro("-15.1",0,FLOOR,"-16"); - test_ro("999999999999999999999.999", 0, CEILING,"1000000000000000000000"); - test_ro("-999999999999999999999.999", 0, FLOOR,"-1000000000000000000000"); + test_ro("999999999", -9, HALF_UP, "1000000000", 0); + test_ro("15.1",0,HALF_EVEN,"15", 0); + test_ro("15.5",0,HALF_EVEN,"16", 0); + test_ro("14.5",0,HALF_EVEN,"14", 0); + test_ro("15.9",0,HALF_EVEN,"16", 0); + test_ro("15.1",0,CEILING,"16", 0); + test_ro("-15.1",0,CEILING,"-15", 0); + test_ro("15.1",0,FLOOR,"15", 0); + test_ro("-15.1",0,FLOOR,"-16", 0); + test_ro("999999999999999999999.999", 0, CEILING,"1000000000000000000000", 0); + test_ro("-999999999999999999999.999", 0, FLOOR,"-1000000000000000000000", 0); + + printf("==== max_decimal ====\n"); + test_mx(1,1,"0.9"); + test_mx(1,0,"9"); + test_mx(2,1,"9.9"); + test_mx(4,2,"99.99"); + test_mx(6,3,"999.999"); + test_mx(8,4,"9999.9999"); + test_mx(10,5,"99999.99999"); + test_mx(12,6,"999999.999999"); + test_mx(14,7,"9999999.9999999"); + test_mx(16,8,"99999999.99999999"); + test_mx(18,9,"999999999.999999999"); + test_mx(20,10,"9999999999.9999999999"); + test_mx(20,20,"0.99999999999999999999"); + test_mx(20,0,"99999999999999999999"); + test_mx(40,20,"99999999999999999999.99999999999999999999"); + + printf("==== decimal2string ====\n"); + test_pr("123.123", 0, 0, 0, "123.123", 0); + test_pr("123.123", 7, 3, '0', "123.123", 0); + test_pr("123.123", 9, 3, '0', "00123.123", 0); + test_pr("123.123", 9, 4, '0', "0123.1230", 0); + test_pr("123.123", 9, 5, '0', "123.12300", 0); + test_pr("123.123", 9, 2, '0', "000123.12", 1); + test_pr("123.123", 9, 6, '0', "23.123000", 2); + + printf("==== decimal_shift ====\n"); + test_sh("123.123", 1, "1231.23", 0); + test_sh("123457189.123123456789000", 1, "1234571891.23123456789", 0); + test_sh("123457189.123123456789000", 4, "1234571891231.23456789", 0); + test_sh("123457189.123123456789000", 8, "12345718912312345.6789", 0); + test_sh("123457189.123123456789000", 9, "123457189123123456.789", 0); + test_sh("123457189.123123456789000", 10, "1234571891231234567.89", 0); + test_sh("123457189.123123456789000", 17, "12345718912312345678900000", 0); + test_sh("123457189.123123456789000", 18, "123457189123123456789000000", 0); + test_sh("123457189.123123456789000", 19, "1234571891231234567890000000", 0); + test_sh("123457189.123123456789000", 26, "12345718912312345678900000000000000", 0); + test_sh("123457189.123123456789000", 27, "123457189123123456789000000000000000", 0); + test_sh("123457189.123123456789000", 28, "1234571891231234567890000000000000000", 0); + test_sh("000000000000000000000000123457189.123123456789000", 26, "12345718912312345678900000000000000", 0); + test_sh("00000000123457189.123123456789000", 27, "123457189123123456789000000000000000", 0); + test_sh("00000000000000000123457189.123123456789000", 28, "1234571891231234567890000000000000000", 0); + test_sh("123", 1, "1230", 0); + test_sh("123", 10, "1230000000000", 0); + test_sh(".123", 1, "1.23", 0); + test_sh(".123", 10, "1230000000", 0); + test_sh(".123", 14, "12300000000000", 0); + test_sh("000.000", 1000, "0", 0); + test_sh("000.", 1000, "0", 0); + test_sh(".000", 1000, "0", 0); + test_sh("1", 1000, "1", 2); + test_sh("123.123", -1, "12.3123", 0); + test_sh("123987654321.123456789000", -1, "12398765432.1123456789", 0); + test_sh("123987654321.123456789000", -2, "1239876543.21123456789", 0); + test_sh("123987654321.123456789000", -3, "123987654.321123456789", 0); + test_sh("123987654321.123456789000", -8, "1239.87654321123456789", 0); + test_sh("123987654321.123456789000", -9, "123.987654321123456789", 0); + test_sh("123987654321.123456789000", -10, "12.3987654321123456789", 0); + test_sh("123987654321.123456789000", -11, "1.23987654321123456789", 0); + test_sh("123987654321.123456789000", -12, "0.123987654321123456789", 0); + test_sh("123987654321.123456789000", -13, "0.0123987654321123456789", 0); + test_sh("123987654321.123456789000", -14, "0.00123987654321123456789", 0); + test_sh("00000087654321.123456789000", -14, "0.00000087654321123456789", 0); + a.len= 2; + test_sh("123.123", -2, "1.23123", 0); + test_sh("123.123", -3, "0.123123", 0); + test_sh("123.123", -6, "0.000123123", 0); + test_sh("123.123", -7, "0.0000123123", 0); + test_sh("123.123", -15, "0.000000000000123123", 0); + test_sh("123.123", -16, "0.000000000000012312", 1); + test_sh("123.123", -17, "0.000000000000001231", 1); + test_sh("123.123", -18, "0.000000000000000123", 1); + test_sh("123.123", -19, "0.000000000000000012", 1); + test_sh("123.123", -20, "0.000000000000000001", 1); + test_sh("123.123", -21, "0", 1); + test_sh(".000000000123", -1, "0.0000000000123", 0); + test_sh(".000000000123", -6, "0.000000000000000123", 0); + test_sh(".000000000123", -7, "0.000000000000000012", 1); + test_sh(".000000000123", -8, "0.000000000000000001", 1); + test_sh(".000000000123", -9, "0", 1); + test_sh(".000000000123", 1, "0.00000000123", 0); + test_sh(".000000000123", 8, "0.0123", 0); + test_sh(".000000000123", 9, "0.123", 0); + test_sh(".000000000123", 10, "1.23", 0); + test_sh(".000000000123", 17, "12300000", 0); + test_sh(".000000000123", 18, "123000000", 0); + test_sh(".000000000123", 19, "1230000000", 0); + test_sh(".000000000123", 20, "12300000000", 0); + test_sh(".000000000123", 21, "123000000000", 0); + test_sh(".000000000123", 22, "1230000000000", 0); + test_sh(".000000000123", 23, "12300000000000", 0); + test_sh(".000000000123", 24, "123000000000000", 0); + test_sh(".000000000123", 25, "1230000000000000", 0); + test_sh(".000000000123", 26, "12300000000000000", 0); + test_sh(".000000000123", 27, "123000000000000000", 0); + test_sh(".000000000123", 28, "0.000000000123", 2); + test_sh("123456789.987654321", -1, "12345678.998765432", 1); + test_sh("123456789.987654321", -2, "1234567.899876543", 1); + test_sh("123456789.987654321", -8, "1.234567900", 1); + test_sh("123456789.987654321", -9, "0.123456789987654321", 0); + test_sh("123456789.987654321", -10, "0.012345678998765432", 1); + test_sh("123456789.987654321", -17, "0.000000001234567900", 1); + test_sh("123456789.987654321", -18, "0.000000000123456790", 1); + test_sh("123456789.987654321", -19, "0.000000000012345679", 1); + test_sh("123456789.987654321", -26, "0.000000000000000001", 1); + test_sh("123456789.987654321", -27, "0", 1); + test_sh("123456789.987654321", 1, "1234567900", 1); + test_sh("123456789.987654321", 2, "12345678999", 1); + test_sh("123456789.987654321", 4, "1234567899877", 1); + test_sh("123456789.987654321", 8, "12345678998765432", 1); + test_sh("123456789.987654321", 9, "123456789987654321", 0); + test_sh("123456789.987654321", 10, "123456789.987654321", 2); + test_sh("123456789.987654321", 0, "123456789.987654321", 0); + a.len= sizeof(buf1)/sizeof(dec1); + + printf("==== decimal_optimize_fraction ====\n"); + test_fr("1.123456789000000000", "1.123456789"); + test_fr("1.12345678000000000", "1.12345678"); + test_fr("1.1234567000000000", "1.1234567"); + test_fr("1.123456000000000", "1.123456"); + test_fr("1.12345000000000", "1.12345"); + test_fr("1.1234000000000", "1.1234"); + test_fr("1.123000000000", "1.123"); + test_fr("1.12000000000", "1.12"); + test_fr("1.1000000000", "1.1"); + test_fr("1.000000000", "1"); + test_fr("1.0", "1"); + test_fr("10000000000000000000.0", "10000000000000000000"); return 0; } |