Commit 001224c0 authored by gunnar@mysql.com's avatar gunnar@mysql.com

item_func.cc:

  fix for bug#8461

  BUG 8461 - TRUNCATE returns incorrect result if 2nd argument is negative
  Reason: Both TRUNCATE/ROUND converts INTEGERS to DOUBLE and back to INTEGERS
  Changed the integer routine to work on integers only.
  This bug affects 4.1, 5.0 and 5.1
  Fixing in 4.1 will need to change the routine to handle different types individually.
  5.0 did had different routines for different types already just the INTEGER routine was bad.
parent 4df873e5
...@@ -203,3 +203,18 @@ NULL ...@@ -203,3 +203,18 @@ NULL
Warnings: Warnings:
Error 1365 Division by 0 Error 1365 Division by 0
set sql_mode=''; set sql_mode='';
select round(111,-10);
round(111,-10)
0
select round(-5000111000111000155,-1);
round(-5000111000111000155,-1)
-5000111000111000160
select round(15000111000111000155,-1);
round(15000111000111000155,-1)
15000111000111000160
select truncate(-5000111000111000155,-1);
truncate(-5000111000111000155,-1)
-5000111000111000150
select truncate(15000111000111000155,-1);
truncate(15000111000111000155,-1)
15000111000111000150
...@@ -141,3 +141,17 @@ select log(2,-1); ...@@ -141,3 +141,17 @@ select log(2,-1);
select log(-2,1); select log(-2,1);
set sql_mode=''; set sql_mode='';
#
# Bug #8461 truncate() and round() return false results 2nd argument negative.
#
# round(a,-b) log_10(b) > a
select round(111,-10);
# round on bigint
select round(-5000111000111000155,-1);
# round on unsigned bigint
select round(15000111000111000155,-1);
# truncate on bigint
select truncate(-5000111000111000155,-1);
# truncate on unsigned bigint
select truncate(15000111000111000155,-1);
...@@ -1863,28 +1863,30 @@ longlong Item_func_round::int_op() ...@@ -1863,28 +1863,30 @@ longlong Item_func_round::int_op()
return value; // integer have not digits after point return value; // integer have not digits after point
abs_dec= -dec; abs_dec= -dec;
double tmp; longlong tmp;
/*
tmp2 is here to avoid return the value with 80 bit precision
This will fix that the test round(0.1,1) = round(0.1,1) is true
*/
volatile double tmp2;
tmp= (abs_dec < array_elements(log_10) ? if(abs_dec >= array_elements(log_10_int))
log_10[abs_dec] : pow(10.0, (double) abs_dec)); return 0;
tmp= log_10_int[abs_dec];
if (truncate) if (truncate)
{ {
if (unsigned_flag) if (unsigned_flag)
tmp2= floor(ulonglong2double(value)/tmp)*tmp; value= (ulonglong(value)/tmp)*tmp;
else if (value >= 0)
tmp2= floor(((double)value)/tmp)*tmp;
else else
tmp2= ceil(((double)value)/tmp)*tmp; value= (value/tmp)*tmp;
} }
else else
tmp2= rint(((double)value)/tmp)*tmp; {
return (longlong)tmp2; if (unsigned_flag)
value= ((ulonglong(value)+(tmp>>1))/tmp)*tmp;
else if ( value >= 0)
value= ((value+(tmp>>1))/tmp)*tmp;
else
value= ((value-(tmp>>1))/tmp)*tmp;
}
return value;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment