سؤال

In all other languages (arithmetic engines in general) putting an extra set of parenthesis around operators of same priority does not impact results. But recently in a testing project I noticed that MS SQL server changes the results in those cases. Please take a look at the query below, and let me know if you have any idea (or a setting in SQL Server administration) or any links to MSDN article explaining the behavior.

select (0.55 * 287.61 / 0.66) calc_no_parens
,(0.55 * (287.61 / 0.66)) calc_parens
,round(0.55 * 287.61 / 0.66,2) no_paren_round
,round(0.55 * (287.61 / 0.66),2) paren_round;

Results

Column  Record 1
calc_no_parens  239.6750000
calc_parens     239.67499985
no_paren_round  239.6800000
paren_round     239.67000000

To me, first two of them should return 239.675, and round should give 239.68.

هل كانت مفيدة؟

المحلول

You will get the desired result if you declare each value as Float.

DECLARE @Float1 float, @Float2 float, @Float3 float;
SET @Float1 = 0.55;
SET @Float2 = 287.61;
SET @Float3 = 0.66;

select (@Float1 * @Float2 / @Float3) calc_no_parens
,(@Float1* (@Float2/ @Float3)) calc_parens
,round(@Float1 * @Float2/ @Float3,2) no_paren_round
,round(@Float1* (@Float2/ @Float3),2) paren_round;

Output

calc_no_parens  calc_parens no_paren_round  paren_round
239.675          239.675    239.68           239.68

You may want to see this article: So-called "exact" numerics are not at all exact!

نصائح أخرى

I can see what is happening, but I don't think there is a fix.

SQL calculates and stores each part of the function as a SQL data type (in this case it's a floating point number).

287.61/0.66 produces 435.7727272727272727272727272... which SQL will store as a floating point number to some degree of accuracy, however it isn't exact (after all, it's a floating point number).

For more info on floating point numbers: How is floating point stored? When does it matter?

Habib's answer made me thinking this has to be with decimal data types my columns are using. After a bit of research, I found this Precision, Scale, and Length (Transact-SQL)

As you can see in that article, division operation significantly changes the both scale and precision of resulting decimal. Then I tried an variation of my query, this time adding extra parenthesis around Multiplication operation.

select distinct (0.55 * 287.61 / 0.66) calc_no_parens
,(0.55 * (287.61 / 0.66)) calc_parens_div
,((0.55 * 287.61) / 0.66) calc_parens_mult
,round(0.55 * 287.61 / 0.66,2) no_paren_round
,round(0.55 * (287.61 / 0.66),2) paren_round
,round((0.55 * 287.61) / 0.66,2) paren_round2;

Results

Column              Record 1
calc_no_parens      239.6750000
calc_parens_div     239.67499985
calc_parens_mult    239.6750000
no_paren_round      239.6800000
paren_round         239.67000000
paren_round2        239.6800000

So as long as division is the last operator in the formula we get correct answers. Its not a fix to the problem, but a learning to self in any future testing projects.

When you use numbers SQL try to convert them dynamically:

{

SELECT 
0.55*(287.61 / 0.66) PrecisionError, 
0.55* (CONVERT(NUMERIC(24,12), 287.61) / CONVERT(NUMERIC(24,12), 0.66)) NotPrecisionError

DECLARE @V SQL_VARIANT
SET @V = 0.55*(287.61 / 0.66)

SELECT 
Value = @V
,[TYPE] = CONVERT(SYSNAME, sql_variant_property(@V, 'BaseType')) + '(' + 
          CONVERT(VARCHAR(10), sql_variant_property(@V, 'Precision')) + ',' + 
          CONVERT(VARCHAR(10), sql_variant_property(@V, 'Scale')) + ')'  

SET @V = 0.55 * (CONVERT(NUMERIC(24,14), 287.61) / CONVERT(NUMERIC(24,14), 0.66))
SELECT 
Value = @V
,[TYPE] = CONVERT(SYSNAME, sql_variant_property(@V, 'BaseType')) + '(' + 
          CONVERT(VARCHAR(10), sql_variant_property(@V, 'Precision')) + ',' + 
          CONVERT(VARCHAR(10), sql_variant_property(@V, 'Scale')) + ')'

}

RESULTS

PrecisionError NotPrecisionError

239.67499985 239.6750000000000

Value TYPE

239.67499985 numeric(14,8)

Value TYPE

239.6750000000000 numeric(38,13)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top