Reputation: 21
I have a problem with round in SQL Server 2014: when I round a number to 2 decimal places sometimes the rounded number is different if I cast to float before or not. For example, if I execute:
select round(cast(3.945 as float),2)
select round(3.945,2)
I have:
3.94
3.950
But if I execute:
select round(cast(3.935 as float),2)
select round(3.935,2)
I have:
3.94
3.940
It seems incorrect, rounding 3.935 and 3.945 casting to float before, I obtain the same value. Is this a bug?
Upvotes: 2
Views: 8330
Reputation: 24410
As @ChrisTravers says in his answer the issue with rounding a float is that you're not getting exact arithmetic. i.e. That explains why round(3.945,2)
rounds up to 3.95
whilst round(3.945E0,2)
effectively rounds down to 3.94
.
If you're wondering why you see more than 2 decimal places in some cases, that's because of the type you're dealing with. i.e. 3.94
is a float, so doesn't have a specified number of decimal places; whilst 3.950
is the result of rounding a decimal(4,3)
; which even though we've rounded to 2 decimal places doesn't affect the precision of the type (i.e. it's still decimal(4,3)
; not converted to decimal(4,2)
or decimal(3,2)
).
If the purpose of this rounding is for display purposes, you're best of using the str
function. i.e.
select str(3.945,4,2) --decimal
select str(3.945E0,4,2) --float
In the above the 4 is the length of the string (i.e. includes the decimal point as a character), and the 2 is the number of decimal places to show.
NB: In this scenario you're chaning the data type to varchar(4)
.
The below code allows you to see what type you get after performing an operation:
declare @result sql_variant = str(3.945E0,4,2)
select sql_variant_property(@result, 'BaseType') [BaseType]
,sql_variant_property(@result, 'MaxLength') [MaxLength]
,sql_variant_property(@result, 'Precision') [Precision]
,sql_variant_property(@result, 'Scale') [Scale]
Upvotes: 0
Reputation: 26454
The problem here is that float is a binary floating point type, where the representation is an approximation of the value. Floats do not losslessly convert to or from base 10, because there is no power of 10 that is also a power of 2. So when this is converted it is done in a way that leaves a roundoff error that pushes the value just before the rounding threshold.
Oddly I cannot reproduce the same behaviour on PostgreSQL and I am not entirely sure why (it may be that on PostgreSQL, round takes a numeric value and this forces a conversion back).
Never use floats where absolute accuracy is required. This occurs not only in databases, but in almost every programming language as well.
Upvotes: 2