Is it possible, in MySQL, to round half a specific way like PHP would do?
PHP_ROUND_HALF_UP
PHP_ROUND_HALF_DOWN
P
Based on MySQL official documentation, the rounding function act as the following:
For exact-value numbers, ROUND() uses the “round half away from zero” or “round toward nearest” rule: A value with a fractional part of .5 or greater is rounded up to the next integer if positive or down to the next integer if negative. (In other words, it is rounded away from zero.) A value with a fractional part less than .5 is rounded down to the next integer if positive or up to the next integer if negative.
http://dev.mysql.com/doc/refman/5.7/en/mathematical-functions.html#function_round
which means that we can only use the rounding function to round up. I have written the following UDF to work around this limitation.
CREATE FUNCTION roundHalf (
numberToRound DECIMAL(20,6),
roundingPrecision TINYINT(2),
roundingType ENUM (
'ROUND_HALF_UP',
'ROUND_HALF_DOWN',
'ROUND_HALF_EVEN',
'ROUND_HALF_ODD'
)
)
RETURNS DECIMAL(20,6)
BEGIN
DECLARE digitEvenOdd TINYINT (2) UNSIGNED DEFAULT 255;
DECLARE digitPosition TINYINT (2) UNSIGNED DEFAULT 0;
DECLARE digitToRound TINYINT (2) DEFAULT -1;
DECLARE roundedNumber DECIMAL(20,6) DEFAULT 0;
SET digitPosition = INSTR(numberToRound, '.');
IF (roundingPrecision < 1) THEN
SET digitPosition = digitPosition + roundingPrecision;
ELSE
SET digitPosition = digitPosition + roundingPrecision + 1;
END IF;
IF (digitPosition > 0 AND
digitPosition <= CHAR_LENGTH(numberToRound)
) THEN
SET digitToRound = CAST(
SUBSTR(
numberToRound,
digitPosition,
1
) AS UNSIGNED
);
SET digitPosition = digitPosition - 1;
IF (digitPosition > 0 AND
digitPosition <= CHAR_LENGTH(numberToRound)
) THEN
SET digitEvenOdd = CAST(
SUBSTR(
numberToRound,
digitPosition,
1
) AS UNSIGNED
);
END IF;
END IF;
IF (digitToRound > -1) THEN
CASE roundingType
WHEN 'ROUND_HALF_UP' THEN
IF (digitToRound >= 5) THEN
SET roundedNumber = ROUND(numberToRound, roundingPrecision);
ELSE
SET roundedNumber = TRUNCATE(numberToRound, roundingPrecision);
END IF;
WHEN 'ROUND_HALF_DOWN' THEN
IF (digitToRound > 5) THEN
SET roundedNumber = ROUND(numberToRound, roundingPrecision);
ELSE
SET roundedNumber = TRUNCATE(numberToRound, roundingPrecision);
END IF;
WHEN 'ROUND_HALF_EVEN' THEN
IF (digitToRound >= 5 AND
digitEvenOdd IN (1,3,5,7,9)
) THEN
SET roundedNumber = ROUND(numberToRound, roundingPrecision);
ELSE
SET roundedNumber = TRUNCATE(numberToRound, roundingPrecision);
END IF;
WHEN 'ROUND_HALF_ODD' THEN
IF (digitToRound >= 5 AND
digitEvenOdd IN (0,2,4,6,8)
) THEN
SET roundedNumber = ROUND(numberToRound, roundingPrecision);
ELSE
SET roundedNumber = TRUNCATE(numberToRound, roundingPrecision);
END IF;
END CASE;
ELSEIF (roundingPrecision > 0) THEN
SET roundedNumber = numberToRound;
END IF;
RETURN roundedNumber;
END //
Note: The value the number to round can be increased based on the need of your project up to 65 digits in total (in that case, do not forget to change all the instances of DECIMAL(20,6)
accordingly).
https://stackoverflow.com/a/19201329/4949388
http://sandbox.onlinephpfunctions.com/code/054de06b074c2b3ece5fb6e5d4180524cd2207e2
/* round number not enough digits */
IF (roundHalf(1.455, 7, 'ROUND_HALF_UP') <> 1.455) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_1';
ELSEIF (roundHalf(1.455, -5, 'ROUND_HALF_UP') <> 0) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_2';
ELSEIF (roundHalf(555, -1, 'ROUND_HALF_UP') <> 560) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_3';
END IF;
/* round half up */
IF (roundHalf(1.541, 2, 'ROUND_HALF_UP') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_1';
ELSEIF (roundHalf(1.545, 2, 'ROUND_HALF_UP') <> 1.55) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_2';
ELSEIF (roundHalf(555, 0, 'ROUND_HALF_UP') <> 555) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_3';
ELSEIF (roundHalf(1000999, -2, 'ROUND_HALF_UP') <> 1001000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_4';
ELSEIF (roundHalf(1000999, -3, 'ROUND_HALF_UP') <> 1001000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_5';
ELSEIF (roundHalf(1000999, -4, 'ROUND_HALF_UP') <> 1000000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_6';
END IF;
/* round half down */
IF (roundHalf(1.541, 2, 'ROUND_HALF_DOWN') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_DOWN_TEST_1';
ELSEIF (roundHalf(1.545, 2, 'ROUND_HALF_DOWN') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_DOWN_TEST_2';
ELSEIF (roundHalf(555, 0, 'ROUND_HALF_DOWN') <> 555) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_DOWN_TEST_3';
ELSEIF (roundHalf(1000999, -2, 'ROUND_HALF_DOWN') <> 1001000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_UP_TEST_4';
ELSEIF (roundHalf(1000999, -3, 'ROUND_HALF_DOWN') <> 1001000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_DOWN_TEST_5';
ELSEIF (roundHalf(1000999, -4, 'ROUND_HALF_DOWN') <> 1000000) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_DOWN_TEST_6';
END IF;
/* round half even */
IF (roundHalf(1.541, 2, 'ROUND_HALF_EVEN') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_EVEN_TEST_1';
ELSEIF (roundHalf(1.544, 2, 'ROUND_HALF_EVEN') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_EVEN_TEST_2';
ELSEIF (roundHalf(1.455, 2, 'ROUND_HALF_EVEN') <> 1.46) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_EVEN_TEST_3';
END IF;
/* round half odd */
IF (roundHalf(1.544, 2, 'ROUND_HALF_ODD') <> 1.54) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_1';
ELSEIF (roundHalf(1.545, 2, 'ROUND_HALF_ODD') <> 1.55) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_2';
ELSEIF (roundHalf(1.455, 2, 'ROUND_HALF_ODD') <> 1.45) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'INVALID_ROUND_HALF_ODD_TEST_3';
END IF;
Use that coding as you please, but don't forget to like my post. Thank you all for your comments and suggestions.