From 001224c09d2423a4c0357e37089de8bd03ad1f8e Mon Sep 17 00:00:00 2001 From: "gunnar@mysql.com." <> Date: Tue, 7 Feb 2006 13:26:35 +0100 Subject: [PATCH] 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. --- mysql-test/r/func_math.result | 15 +++++++++++++++ mysql-test/t/func_math.test | 14 ++++++++++++++ sql/item_func.cc | 34 ++++++++++++++++++---------------- 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/mysql-test/r/func_math.result b/mysql-test/r/func_math.result index fba274b9bb1..1507f959ae6 100644 --- a/mysql-test/r/func_math.result +++ b/mysql-test/r/func_math.result @@ -203,3 +203,18 @@ NULL Warnings: Error 1365 Division by 0 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 diff --git a/mysql-test/t/func_math.test b/mysql-test/t/func_math.test index 9cf0ee452cd..8dc4eb215c7 100644 --- a/mysql-test/t/func_math.test +++ b/mysql-test/t/func_math.test @@ -141,3 +141,17 @@ select log(2,-1); select log(-2,1); 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); + diff --git a/sql/item_func.cc b/sql/item_func.cc index 2d595e5fdc8..a85f05c2e22 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -1863,28 +1863,30 @@ longlong Item_func_round::int_op() return value; // integer have not digits after point abs_dec= -dec; - double 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) ? - log_10[abs_dec] : pow(10.0, (double) abs_dec)); - + longlong tmp; + + if(abs_dec >= array_elements(log_10_int)) + return 0; + + tmp= log_10_int[abs_dec]; + if (truncate) { if (unsigned_flag) - tmp2= floor(ulonglong2double(value)/tmp)*tmp; - else if (value >= 0) - tmp2= floor(((double)value)/tmp)*tmp; + value= (ulonglong(value)/tmp)*tmp; else - tmp2= ceil(((double)value)/tmp)*tmp; + value= (value/tmp)*tmp; } 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; }