diff --git a/mysql-test/r/func_math.result b/mysql-test/r/func_math.result
index 565c283ee839bbfa23306de2617f6eeb86429d78..e2e93796afe07863313245dce8863e14d33ba854 100644
--- a/mysql-test/r/func_math.result
+++ b/mysql-test/r/func_math.result
@@ -155,9 +155,6 @@ select format(col2,6) from t1 where col1=7;
 format(col2,6)
 1,234,567,890,123,456.123450
 drop table t1;
-select round(150, 2);
-round(150, 2)
-150.00
 select ceil(0.09);
 ceil(0.09)
 1
@@ -168,11 +165,11 @@ create table t1 select round(1, 6);
 show create table t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
-  `round(1, 6)` decimal(7,6) NOT NULL DEFAULT '0.000000'
+  `round(1, 6)` int(1) NOT NULL DEFAULT '0'
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1
 select * from t1;
 round(1, 6)
-1.000000
+1
 drop table t1;
 select abs(-2) * -2;
 abs(-2) * -2
@@ -285,3 +282,91 @@ format(t2.f2-t2.f1+1,0)
 10,000
 drop table t1, t2;
 set names default;
+select cast(-2 as unsigned), 18446744073709551614, -2;
+cast(-2 as unsigned)	18446744073709551614	-2
+18446744073709551614	18446744073709551614	-2
+select abs(cast(-2 as unsigned)), abs(18446744073709551614), abs(-2);
+abs(cast(-2 as unsigned))	abs(18446744073709551614)	abs(-2)
+18446744073709551614	18446744073709551614	2
+select ceiling(cast(-2 as unsigned)), ceiling(18446744073709551614), ceiling(-2);
+ceiling(cast(-2 as unsigned))	ceiling(18446744073709551614)	ceiling(-2)
+18446744073709551614	18446744073709551614	-2
+select floor(cast(-2 as unsigned)), floor(18446744073709551614), floor(-2);
+floor(cast(-2 as unsigned))	floor(18446744073709551614)	floor(-2)
+18446744073709551614	18446744073709551614	-2
+select format(cast(-2 as unsigned), 2), format(18446744073709551614, 2), format(-2, 2);
+format(cast(-2 as unsigned), 2)	format(18446744073709551614, 2)	format(-2, 2)
+18,446,744,073,709,551,614.00	18,446,744,073,709,551,614.00	-2.00
+select sqrt(cast(-2 as unsigned)), sqrt(18446744073709551614), sqrt(-2);
+sqrt(cast(-2 as unsigned))	sqrt(18446744073709551614)	sqrt(-2)
+4294967296	4294967296	NULL
+select round(cast(-2 as unsigned), 1), round(18446744073709551614, 1), round(-2, 1);
+round(cast(-2 as unsigned), 1)	round(18446744073709551614, 1)	round(-2, 1)
+18446744073709551614	18446744073709551614	-2
+select round(4, cast(-2 as unsigned)), round(4, 18446744073709551614), round(4, -2);
+round(4, cast(-2 as unsigned))	round(4, 18446744073709551614)	round(4, -2)
+4	4	0
+select truncate(cast(-2 as unsigned), 1), truncate(18446744073709551614, 1), truncate(-2, 1);
+truncate(cast(-2 as unsigned), 1)	truncate(18446744073709551614, 1)	truncate(-2, 1)
+18446744073709551614	18446744073709551614	-2
+select truncate(4, cast(-2 as unsigned)), truncate(4, 18446744073709551614), truncate(4, -2);
+truncate(4, cast(-2 as unsigned))	truncate(4, 18446744073709551614)	truncate(4, -2)
+4	4	0
+select round(10000000000000000000, -19), truncate(10000000000000000000, -19);
+round(10000000000000000000, -19)	truncate(10000000000000000000, -19)
+10000000000000000000	10000000000000000000
+select round(1e0, -309), truncate(1e0, -309);
+round(1e0, -309)	truncate(1e0, -309)
+0	0
+select round(1e1,308), truncate(1e1, 308);
+round(1e1,308)	truncate(1e1, 308)
+10	10
+select round(1e1, 2147483648), truncate(1e1, 2147483648);
+round(1e1, 2147483648)	truncate(1e1, 2147483648)
+10	10
+select round(1.1e1, 4294967295), truncate(1.1e1, 4294967295);
+round(1.1e1, 4294967295)	truncate(1.1e1, 4294967295)
+11	11
+select round(1.12e1, 4294967296), truncate(1.12e1, 4294967296);
+round(1.12e1, 4294967296)	truncate(1.12e1, 4294967296)
+11.2	11.2
+select round(1.5, 2147483640), truncate(1.5, 2147483640);
+round(1.5, 2147483640)	truncate(1.5, 2147483640)
+1.500000000000000000000000000000	1.500000000000000000000000000000
+select round(1.5, -2147483649), round(1.5, 2147483648);
+round(1.5, -2147483649)	round(1.5, 2147483648)
+0	1.500000000000000000000000000000
+select truncate(1.5, -2147483649), truncate(1.5, 2147483648);
+truncate(1.5, -2147483649)	truncate(1.5, 2147483648)
+0	1.500000000000000000000000000000
+select round(1.5, -4294967296), round(1.5, 4294967296);
+round(1.5, -4294967296)	round(1.5, 4294967296)
+0	1.500000000000000000000000000000
+select truncate(1.5, -4294967296), truncate(1.5, 4294967296);
+truncate(1.5, -4294967296)	truncate(1.5, 4294967296)
+0	1.500000000000000000000000000000
+select round(1.5, -9223372036854775808), round(1.5, 9223372036854775808);
+round(1.5, -9223372036854775808)	round(1.5, 9223372036854775808)
+0	1.500000000000000000000000000000
+select truncate(1.5, -9223372036854775808), truncate(1.5, 9223372036854775808);
+truncate(1.5, -9223372036854775808)	truncate(1.5, 9223372036854775808)
+0	1.500000000000000000000000000000
+select round(1.5, 18446744073709551615), truncate(1.5, 18446744073709551615);
+round(1.5, 18446744073709551615)	truncate(1.5, 18446744073709551615)
+1.500000000000000000000000000000	1.500000000000000000000000000000
+select round(18446744073709551614, -1), truncate(18446744073709551614, -1);
+round(18446744073709551614, -1)	truncate(18446744073709551614, -1)
+18446744073709551610	18446744073709551610
+select round(4, -4294967200), truncate(4, -4294967200);
+round(4, -4294967200)	truncate(4, -4294967200)
+0	0
+select mod(cast(-2 as unsigned), 3), mod(18446744073709551614, 3), mod(-2, 3);
+mod(cast(-2 as unsigned), 3)	mod(18446744073709551614, 3)	mod(-2, 3)
+2	2	-2
+select mod(5, cast(-2 as unsigned)), mod(5, 18446744073709551614), mod(5, -2);
+mod(5, cast(-2 as unsigned))	mod(5, 18446744073709551614)	mod(5, -2)
+5	5	1
+select pow(cast(-2 as unsigned), 5), pow(18446744073709551614, 5), pow(-2, 5);
+pow(cast(-2 as unsigned), 5)	pow(18446744073709551614, 5)	pow(-2, 5)
+2.1359870359209e+96	2.1359870359209e+96	-32
+End of 5.0 tests
diff --git a/mysql-test/r/type_newdecimal.result b/mysql-test/r/type_newdecimal.result
index f3c7e45de392b20c1dde0648f729c810952e9674..04baa03ce4d263134527f672126c7905bd6c729b 100644
--- a/mysql-test/r/type_newdecimal.result
+++ b/mysql-test/r/type_newdecimal.result
@@ -763,7 +763,7 @@ truncate(99999999999999999999999999999999999999,31)
 99999999999999999999999999999999999999.000000000000000000000000000000
 select truncate(99.999999999999999999999999999999999999,31);
 truncate(99.999999999999999999999999999999999999,31)
-100.000000000000000000000000000000
+99.999999999999999999999999999999
 select truncate(99999999999999999999999999999999999999,-31);
 truncate(99999999999999999999999999999999999999,-31)
 99999990000000000000000000000000000000
diff --git a/mysql-test/t/func_math.test b/mysql-test/t/func_math.test
index 639ced6a1c021dade325975a82a465083c2d4c76..668528b2e9bd42230d5dc4681269ca86abe6a2e4 100644
--- a/mysql-test/t/func_math.test
+++ b/mysql-test/t/func_math.test
@@ -87,11 +87,6 @@ select format(col2,6) from t1 where col1=7;
 drop table t1;
 
 
-#
-# Bug #10083 (round doesn't increase decimals)
-#
-select round(150, 2);
-
 #
 # Bug @10632 (Ceiling function returns wrong answer)
 #
@@ -196,3 +191,37 @@ select format(t2.f2-t2.f1+1,0) from t1,t2
 where t1.f2 = t2.f3 order by t1.f1;
 drop table t1, t2;
 set names default;
+
+# Bug 24912 -- misc functions have trouble with unsigned
+
+select cast(-2 as unsigned), 18446744073709551614, -2;
+select abs(cast(-2 as unsigned)), abs(18446744073709551614), abs(-2);
+select ceiling(cast(-2 as unsigned)), ceiling(18446744073709551614), ceiling(-2);
+select floor(cast(-2 as unsigned)), floor(18446744073709551614), floor(-2);
+select format(cast(-2 as unsigned), 2), format(18446744073709551614, 2), format(-2, 2);
+select sqrt(cast(-2 as unsigned)), sqrt(18446744073709551614), sqrt(-2);
+select round(cast(-2 as unsigned), 1), round(18446744073709551614, 1), round(-2, 1);
+select round(4, cast(-2 as unsigned)), round(4, 18446744073709551614), round(4, -2);
+select truncate(cast(-2 as unsigned), 1), truncate(18446744073709551614, 1), truncate(-2, 1);
+select truncate(4, cast(-2 as unsigned)), truncate(4, 18446744073709551614), truncate(4, -2);
+select round(10000000000000000000, -19), truncate(10000000000000000000, -19);
+select round(1e0, -309), truncate(1e0, -309);
+select round(1e1,308), truncate(1e1, 308);
+select round(1e1, 2147483648), truncate(1e1, 2147483648);
+select round(1.1e1, 4294967295), truncate(1.1e1, 4294967295);
+select round(1.12e1, 4294967296), truncate(1.12e1, 4294967296);
+select round(1.5, 2147483640), truncate(1.5, 2147483640);
+select round(1.5, -2147483649), round(1.5, 2147483648);
+select truncate(1.5, -2147483649), truncate(1.5, 2147483648);
+select round(1.5, -4294967296), round(1.5, 4294967296);
+select truncate(1.5, -4294967296), truncate(1.5, 4294967296);
+select round(1.5, -9223372036854775808), round(1.5, 9223372036854775808);
+select truncate(1.5, -9223372036854775808), truncate(1.5, 9223372036854775808);
+select round(1.5, 18446744073709551615), truncate(1.5, 18446744073709551615);
+select round(18446744073709551614, -1), truncate(18446744073709551614, -1);
+select round(4, -4294967200), truncate(4, -4294967200);
+select mod(cast(-2 as unsigned), 3), mod(18446744073709551614, 3), mod(-2, 3);
+select mod(5, cast(-2 as unsigned)), mod(5, 18446744073709551614), mod(5, -2);
+select pow(cast(-2 as unsigned), 5), pow(18446744073709551614, 5), pow(-2, 5);
+
+--echo End of 5.0 tests
diff --git a/sql/item_func.cc b/sql/item_func.cc
index c823575b240f2e7d397e285ac963354a866bb576..c823fe12d2504814b429fd2f49fca58885b87171 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -619,6 +619,14 @@ Item *Item_func::get_tmp_table_item(THD *thd)
   return copy_or_same(thd);
 }
 
+double Item_int_func::val_real()
+{
+  DBUG_ASSERT(fixed == 1);
+
+  return unsigned_flag ? (double) ((ulonglong) val_int()) : (double) val_int();
+}
+
+
 String *Item_int_func::val_str(String *str)
 {
   DBUG_ASSERT(fixed == 1);
@@ -801,7 +809,10 @@ double Item_func_numhybrid::val_real()
     return result;
   }
   case INT_RESULT:
-    return (double)int_op();
+  {
+    longlong result= int_op();
+    return unsigned_flag ? (double) ((ulonglong) result) : (double) result;
+  }
   case REAL_RESULT:
     return real_op();
   case STRING_RESULT:
@@ -1361,6 +1372,8 @@ longlong Item_func_mod::int_op()
   DBUG_ASSERT(fixed == 1);
   longlong value=  args[0]->val_int();
   longlong val2= args[1]->val_int();
+  longlong result;
+
   if ((null_value= args[0]->null_value || args[1]->null_value))
     return 0; /* purecov: inspected */
   if (val2 == 0)
@@ -1370,9 +1383,13 @@ longlong Item_func_mod::int_op()
   }
 
   if (args[0]->unsigned_flag)
-    return ((ulonglong) value) % val2;
+    result= args[1]->unsigned_flag ? 
+      ((ulonglong) value) % ((ulonglong) val2) : ((ulonglong) value) % val2;
+  else
+    result= args[1]->unsigned_flag ?
+      value % ((ulonglong) val2) : value % val2;
 
-  return value % val2;
+  return result;
 }
 
 double Item_func_mod::real_op()
@@ -1427,6 +1444,7 @@ void Item_func_mod::fix_length_and_dec()
 {
   Item_num_op::fix_length_and_dec();
   maybe_null= 1;
+  unsigned_flag= args[0]->unsigned_flag;
 }
 
 
@@ -1505,8 +1523,9 @@ double Item_func_abs::real_op()
 longlong Item_func_abs::int_op()
 {
   longlong value= args[0]->val_int();
-  null_value= args[0]->null_value;
-  return value >= 0 ? value : -value;
+  if ((null_value= args[0]->null_value))
+    return 0;
+  return (value >= 0) || unsigned_flag ? value : -value;
 }
 
 
@@ -1527,6 +1546,7 @@ my_decimal *Item_func_abs::decimal_op(my_decimal *decimal_value)
 void Item_func_abs::fix_length_and_dec()
 {
   Item_func_num1::fix_length_and_dec();
+  unsigned_flag= args[0]->unsigned_flag;
 }
 
 
@@ -1901,6 +1921,10 @@ my_decimal *Item_func_floor::decimal_op(my_decimal *decimal_value)
 
 void Item_func_round::fix_length_and_dec()
 {
+  int      decimals_to_set;
+  longlong val1;
+  bool     val1_unsigned;
+  
   unsigned_flag= args[0]->unsigned_flag;
   if (!args[1]->const_item())
   {
@@ -1909,8 +1933,14 @@ void Item_func_round::fix_length_and_dec()
     hybrid_type= REAL_RESULT;
     return;
   }
-  
-  int decimals_to_set= max((int)args[1]->val_int(), 0);
+
+  val1= args[1]->val_int();
+  val1_unsigned= args[1]->unsigned_flag;
+  if (val1 < 0)
+    decimals_to_set= val1_unsigned ? INT_MAX : 0;
+  else
+    decimals_to_set= (val1 > INT_MAX) ? INT_MAX : (int) val1;
+
   if (args[0]->decimals == NOT_FIXED_DEC)
   {
     max_length= args[0]->max_length;
@@ -1927,10 +1957,9 @@ void Item_func_round::fix_length_and_dec()
     max_length= float_length(decimals);
     break;
   case INT_RESULT:
-    if (!decimals_to_set &&
-        (truncate || (args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS)))
+    if ((!decimals_to_set && truncate) || (args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS))
     {
-      int length_can_increase= test(!truncate && (args[1]->val_int() < 0));
+      int length_can_increase= test(!truncate && (val1 < 0) && !val1_unsigned);
       max_length= args[0]->max_length + length_can_increase;
       /* Here we can keep INT_RESULT */
       hybrid_type= INT_RESULT;
@@ -1956,10 +1985,12 @@ void Item_func_round::fix_length_and_dec()
   }
 }
 
-double my_double_round(double value, int dec, bool truncate)
+double my_double_round(double value, longlong dec, bool dec_unsigned,
+                       bool truncate)
 {
   double tmp;
-  uint abs_dec= abs(dec);
+  bool dec_negative= (dec < 0) && !dec_unsigned;
+  ulonglong abs_dec= dec_negative ? -dec : dec;
   /*
     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
@@ -1969,7 +2000,11 @@ double my_double_round(double value, int dec, bool truncate)
   tmp=(abs_dec < array_elements(log_10) ?
        log_10[abs_dec] : pow(10.0,(double) abs_dec));
 
-  if (truncate)
+  if (dec_negative && isinf(tmp))
+    tmp2= 0;
+  else if (!dec_negative && isinf(value * tmp))
+    tmp2= value;
+  else if (truncate)
   {
     if (value >= 0)
       tmp2= dec < 0 ? floor(value/tmp)*tmp : floor(value*tmp)/tmp;
@@ -1985,24 +2020,35 @@ double my_double_round(double value, int dec, bool truncate)
 double Item_func_round::real_op()
 {
   double value= args[0]->val_real();
-  int dec= (int) args[1]->val_int();
 
   if (!(null_value= args[0]->null_value || args[1]->null_value))
-    return my_double_round(value, dec, truncate);
+    return my_double_round(value, args[1]->val_int(), args[1]->unsigned_flag,
+                           truncate);
 
   return 0.0;
 }
 
+/*
+  Rounds a given value to a power of 10 specified as the 'to' argument,
+  avoiding overflows when the value is close to the ulonglong range boundary.
+*/
+
+static inline ulonglong my_unsigned_round(ulonglong value, ulonglong to)
+{
+  ulonglong tmp= value / to * to;
+  return (value - tmp < (to >> 1)) ? tmp : tmp + to;
+}
+
 
 longlong Item_func_round::int_op()
 {
   longlong value= args[0]->val_int();
-  int dec=(int) args[1]->val_int();
+  longlong dec= args[1]->val_int();
   decimals= 0;
-  uint abs_dec;
+  ulonglong abs_dec;
   if ((null_value= args[0]->null_value || args[1]->null_value))
     return 0;
-  if (dec >= 0)
+  if ((dec >= 0) || args[1]->unsigned_flag)
     return value; // integer have not digits after point
 
   abs_dec= -dec;
@@ -2014,21 +2060,12 @@ longlong Item_func_round::int_op()
   tmp= log_10_int[abs_dec];
   
   if (truncate)
-  {
-    if (unsigned_flag)
-      value= (ulonglong(value)/tmp)*tmp;
-    else
-      value= (value/tmp)*tmp;
-  }
+    value= (unsigned_flag) ?
+      ((ulonglong) value / tmp) * tmp : (value / tmp) * tmp;
   else
-  {
-    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;
-  }
+    value= (unsigned_flag || value >= 0) ?
+      my_unsigned_round((ulonglong) value, tmp) :
+      -my_unsigned_round((ulonglong) -value, tmp);
   return value;
 }
 
@@ -2036,14 +2073,18 @@ longlong Item_func_round::int_op()
 my_decimal *Item_func_round::decimal_op(my_decimal *decimal_value)
 {
   my_decimal val, *value= args[0]->val_decimal(&val);
-  int dec=(int) args[1]->val_int();
-  if (dec > 0)
+  longlong dec= args[1]->val_int();
+  if (dec > 0 || (dec < 0 && args[1]->unsigned_flag))
   {
-    decimals= min(dec, DECIMAL_MAX_SCALE); // to get correct output
+    dec= min((ulonglong) dec, DECIMAL_MAX_SCALE);
+    decimals= dec; // to get correct output
   }
+  else if (dec < INT_MIN)
+    dec= INT_MIN;
+    
   if (!(null_value= (args[0]->null_value || args[1]->null_value ||
-                     my_decimal_round(E_DEC_FATAL_ERROR, value, dec, truncate,
-                                      decimal_value) > 1)))
+                     my_decimal_round(E_DEC_FATAL_ERROR, value, dec,
+                                      truncate, decimal_value) > 1)))
     return decimal_value;
   return 0;
 }
diff --git a/sql/item_func.h b/sql/item_func.h
index 3306b0590971dea5c430e1b85a3d8433423ee23a..f1e3295481c3e39595f97e2da7fdcae860731a0d 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -280,7 +280,7 @@ public:
   { max_length= 21; }
   Item_int_func(List<Item> &list) :Item_func(list) { max_length= 21; }
   Item_int_func(THD *thd, Item_int_func *item) :Item_func(thd, item) {}
-  double val_real() { DBUG_ASSERT(fixed == 1); return (double) val_int(); }
+  double val_real();
   String *val_str(String*str);
   enum Item_result result_type () const { return INT_RESULT; }
   void fix_length_and_dec() {}
@@ -305,12 +305,6 @@ class Item_func_signed :public Item_int_func
 public:
   Item_func_signed(Item *a) :Item_int_func(a) {}
   const char *func_name() const { return "cast_as_signed"; }
-  double val_real()
-  {
-    double tmp= args[0]->val_real();
-    null_value= args[0]->null_value;
-    return tmp;
-  }
   longlong val_int();
   longlong val_int_from_str(int *error);
   void fix_length_and_dec()
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index a011a2884c810f4fe947fc72f9e04e5a1b0f0098..572ec9727174a0d34222da8ad6c7b8ddadfdc037 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -1951,7 +1951,7 @@ String *Item_func_format::val_str(String *str)
     double nr= args[0]->val_real();
     if ((null_value=args[0]->null_value))
       return 0; /* purecov: inspected */
-    nr= my_double_round(nr, dec, FALSE);
+    nr= my_double_round(nr, (longlong) dec, FALSE, FALSE);
     /* Here default_charset() is right as this is not an automatic conversion */
     str->set_real(nr, dec, default_charset());
     if (isnan(nr))
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 099c9b98c1f92b418dedc15eed8a6f28f3046c43..904d18d690c9713886b1083ff19f2618ab382e51 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -1866,7 +1866,8 @@ ha_rows filesort(THD *thd, TABLE *form,struct st_sort_field *sortorder,
                  ha_rows *examined_rows);
 void filesort_free_buffers(TABLE *table, bool full);
 void change_double_for_sort(double nr,byte *to);
-double my_double_round(double value, int dec, bool truncate);
+double my_double_round(double value, longlong dec, bool dec_unsigned,
+                       bool truncate);
 int get_quick_record(SQL_SELECT *select);
 
 int calc_weekday(long daynr,bool sunday_first_day_of_week);