简体   繁体   中英

Recreating .NET rounding behavior in SQL

I'm trying to migrate a subtle calculation from C#.NET to TSQL and absolutely every result must remain the same, regardless of whether the original C#.NET answer is inaccurate in some cases.

My TSQL script mostly succeeds at this, however, there is some unusual rounding behavior in .NET that I've been unable to capture. I'll demonstrate with two examples:

Here's the .NET code that I'm trying to recreate.

(double)Math.Round(number, 1, MidpointRounding.AwayFromZero );

double number = 6.1499999999999995; // returns 6.1
double number = 6.9499999999999993;  // returns 7  (which, to me, seems inconsistent) 

Here's the TSQL code that I'm trying to align with the .NET code above.

SELECT ROUND(ROUND(CAST(@Score as decimal(10,1)), 16), 1)

DECLARE @Score DECIMAL(18,16) = 6.1499999999999995 -- returns 6.1
DECLARE @Score DECIMAL(18,16) = 6.9499999999999993 -- returns 6.9 (needs to be 7)

My goal is to get my TSQL code to return 7 instead of 6.9 in the second case. Does anyone know how I can make that happen?

decimal is a decimal-accurate number. double isn't. Since your C# code is using double , only real way to replicate a similar behaviour in T-SQL is to also use a binary floating point number in T-SQL - float (specifically, double corresponds to float(53) ). The rounding behaviour isn't "unusual", it follows the IEEE-754 standard.

However, if you care about decimal precision (as it seems you should), you should really push switching the C# code to use decimal instead of double . Binary floating points are a terrible choice when you want decimal precision.

I don't think there a reliable way to get what you want using plain T-SQL.

Your logic is wrong:

CAST(@Score as decimal(10,1)) already rounds before you apply ROUND

DECLARE @Score decimal(18,16) = 6.9499999999999993

SELECT @Score, CAST(@Score as decimal(10,1))

6.9499999999999993 6.9

And you ROUND to 16 digits and then apply another ROUND to 1 digit, but ROUND(ROUND(n, 16), 1) might be a different result than ROUND(@n,1) directly and is definitely not the way .NET rounds.

Another problem is the rounding rule, you specify MidpointRounding.AwayFromZero , but afaik the default for T-SQL is Rounding to Even .

You might check SQL Server Rounding Methods and try to implement Round to Even but then there's still the problem of significant digits, DEC(18,16) has 18, but DOUBLE only 16.

If you can't use a CLR-function in T-SQL or switch to DECIMAL in .NET you're probably doomed...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM