Reputation: 1389
As far as i know according to mathematics rounding should work as below when rounding number is 5.
2.435 => 2.44 (Round Up, if rounding to digit(3) is odd number)
2.445 => 2.44 (Round Down, if rounding to digit(4) is even number)
if we do summation all fine,
2.435 + 2.445 = 4.88
2.44 + 2.44 = 4.88
I'm pretty sure in .Net also rounding works like this.
But in SQL server, 5 is always rounding up which is not correct according to maths.
SELECT round(2.345, 2) = 2.35
SELECT round(2.335, 2) => 2.34
this results to 1 cent discrepancies in summation of rounded values.
2.345 + 2.335 = 4.68
2.35 + 2.34 = 4.69 => which is not correct
I have tried this with decimal and money data types.
Am i doing something wrong? Is there a work around for this?
Upvotes: 6
Views: 15388
Reputation: 8668
Here is our version of banker's rounding, with some simplifications and assumptions on our max monetary amounts:
Could also truncate to decimal(10,3), we just need the input to preserve enough decimal places for the rounding to work.
Adjust the decimal scale and precision to your needs.
The SQL function
CREATE FUNCTION dbo.fnBankersRound(@Num decimal(10,4))
RETURNS decimal(8,2)
AS
BEGIN
RETURN CASE
WHEN ROUND(@Num, 2) - @Num = .005 AND ROUND(@Num, 2) % .02 <> 0
THEN ROUND(@Num, 2) - 0.01
ELSE ROUND(@Num, 2)
END
END
Some tests to validate the rounding
declare @val decimal(10,4);
set @val = 1.1149;
select @val as Value, round(@val, 2) as Round, dbo.fnBankersRound(@val) as BankRound
set @val = 1.1150;
select @val as Value, round(@val, 2) as Round, dbo.fnBankersRound(@val) as BankRound
set @val = 1.1249;
select @val as Value, round(@val, 2) as Round, dbo.fnBankersRound(@val) as BankRound
set @val = 1.1250;
select @val as Value, round(@val, 2) as Round, dbo.fnBankersRound(@val) as BankRound
set @val = 1.1349;
select @val as Value, round(@val, 2) as Round, dbo.fnBankersRound(@val) as BankRound
set @val = 1.1350;
select @val as Value, round(@val, 2) as Round, dbo.fnBankersRound(@val) as BankRound
select
100000000.01 / 800000000.01 as Value,
round(100000000.01 / 800000000.01, 2) as Round,
dbo.fnBankersRound(100000000.01 / 800000000.01) as BankRound
Original post at sqlservercentral.com
Upvotes: 1
Reputation: 266
If you do want to use banker's rounding in SQL Server...
CREATE FUNCTION BankersRounding(@value decimal(36,11), @significantDigits INT)
RETURNS MONEY
AS
BEGIN
-- if value = 12.345 and signficantDigits = 2...
-- base = 1000
declare @base int = power(10, @significantDigits + 1)
-- roundingValue = 12345
declare @roundingValue decimal(36,11) = floor(abs(@value) * @base)
-- roundingDigit = 5
declare @roundingDigit int = @roundingValue % 10
-- significantValue = 1234
declare @significantValue decimal(36,11) = floor(@roundingValue / 10)
-- lastSignificantDigit = 4
declare @lastSignificantDigit int = @significantValue % 10
-- awayFromZero = 12.35
declare @awayFromZero money = (@significantValue + 1) / (@base / 10)
-- towardsZero = 12.34
declare @towardsZero money = @significantValue / (@base / 10)
-- negative values handled slightly different
if @value < 0
begin
-- awayFromZero = -12.35
set @awayFromZero = ((-1 * @significantValue) - 1) / (@base / 10)
-- towardsZero = -12.34
set @towardsZero = (-1 * @significantValue) / (@base / 10)
end
-- default to towards zero (i.e. assume thousandths digit is 0-4)
declare @rv money = @towardsZero
if @roundingDigit > 5
set @rv = @awayFromZero -- 5-9 goes away from 0
else if @roundingDigit = 5
begin
-- 5 goes to nearest even number (towards zero if even, away from zero if odd)
set @rv = case when @lastSignificantDigit % 2 = 0 then @towardsZero else @awayFromZero end
end
return @rv
end
Upvotes: 6
Reputation: 1357
You're looking for Banker's Rounding - which is the default rounding in C# but is not how SQL Server ROUND() works.
See Why does TSQL on Sql Server 2000 round decimals inconsistently? as well as http://blogs.lessthandot.com/index.php/DataMgmt/DataDesign/sql-server-rounding-methods and http://www.chrispoulter.com/blog/post/rounding-decimals-using-net-and-t-sql
Upvotes: 6
Reputation: 810
Mathematically rounding up at 5 is correct, and also the most commonly used type of rounding in basic mathematics. Other types of rounding are also valid, but are not basic mathematics, but more often used in certain areas due to 0.5 often being a dispute number.
What you call mathematically rounding is actually bankers rounding, which is the type of rounding used in the finance business.
Upvotes: 5