SQL Server 2014 rounding a float gives unexpected result - sql

Could anyone explain the result of the following query? I think this is a bug in SQL Server 2014.
DECLARE #x float
SET #x=58.415
SELECT ROUND(58.415,2), ROUND(#x,2)

Because the first argument is stored as decimal(5,3):
EXEC sp_describe_first_result_set N'SELECT 58.415 x', null, 0;
You have two different codes:
DECLARE #x float
SET #x=58.415
SELECT ROUND(58.415,2), ROUND(#x,2)
GO
DECLARE #x decimal(19,3)
SET #x=58.415
SELECT ROUND(58.415,2), ROUND(#x,2)
GO
Basically, the float is
Approximate-number data types for use with floating point numeric
data. Floating point data is approximate; therefore, not all values in
the data type range can be represented exactly.
An improvement made by #Zohar explaining why the value is converted to decimal:
In Transact-SQL statements, a constant with a decimal point is
automatically converted into a numeric data value, using the minimum
precision and scale necessary. For example, the constant 12.345 is
converted into a numeric value with a precision of 5 and a scale of 3

The explanation for what you are seeing is that floating point arithmetic is not exact in SQL Server (or in any other database or programming language). Here is what is actually happening, with the "real" value being shown for explanation:
SELECT
ROUND(58.415, 2), -- rounds UP to 58.420, this is DECIMAL(10,3), EXACT
ROUND(58.4149999999999, 2) -- rounds DOWN to 58.41
The issue here is that when you made the following variable assignment:
DECLARE #x float
SET #x = 58.415
internally, SQL Server actually stored the value as an approximation, something like 58.41499999999. Then, when rounding to two decimal places, you were left with 58.41.
In general, if you require exact precision, you should use an exact type. In this case, DECIMAL(10,3) would work.

Related

How do I convert this value 1.2851048260000018E7 (double) to int datatype in a SQL query?

When I run a SQL query, I get a big exponential value 1.2851048260000018E7 for the dollar amount. How to convert it to regular value?
I'm assuming you're not really looking for int, but rather a non-scientific notation. If you simply want dollars and cents use the following (since you didn't provide sample code/data, you'll have to adjust this for your query):
SELECT CAST(1.2851048260000018E7 AS decimal(18,2))
If you need more decimal digits for calculations, use whatever would be appropriate for scale with the syntax decimal(precision, scale), described here.
If you really are looking for an int datatype, that is dollars with cents rounded, use:
SELECT CAST(ROUND(1.2851048260000018E7, 0) AS int)
Use caution if leaving out the ROUND function. When you CAST a decimal to int, the number will be truncated at the decimal point, not rounded.

SQL Round cast to float

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?
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.
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]

How to convert decimal to scientific in SQL Server 2008 R2

I have a table named Product. The table looks like this:
ID Product Volume
1 xyz 4654.000000000000000
2 abc 121.000000000000000
I want to represent Volume in a scientific notation. Volume is of datatype decimal(20,8).
How can I do this conversion?
Normally, the formatting and presentation of data should be done by UI, on the client side, but if you insist...
DECLARE #t decimal(20,8) = 4654.000000000000000;
SELECT #t, CONVERT(nvarchar(32), CAST(#t as float), 2)
result
4654.00000000 4.654000000000000e+003
First CAST decimal to float (please note, it may loose precision), then CONVERT float to nvarchar using style 2:
Always 16 digits. Always use in scientific notation.
Note
float type has only 15 digits of precision, so it can't store your decimal(20,8) without loss of precision.
declare #t decimal(20,8)=132423423421.00000000
print cast(#t as float)
you can cast volume field to float in select statement and then you will get scientific notation of it as shown above.

SQL - Convert number to decimal

I'm trying to convert a number to a decimal with two decimals places.
SELECT CONVERT(DECIMAL(10,2),12345)
The above would return 12345.00 but I'm trying to achieve 123.45
You need something like that:
SELECT CONVERT(DECIMAL(15,2),12345/100.0)
SELECT CONVERT(DECIMAL(10,2),CAST(12345 as float)/CAST(100 as float))
Correction: The premise is somewhat flawed, as the data type of a literal number without a decimal point is int, not numeric as implied by the question. In that case, you do need to convert the initial value to either numeric or decimal before dividing:
SELECT CONVERT(DECIMAL,12345)/100
or
SELECT CAST(12345 AS DECIMAL)/100
(cast is the SQL standard, so if you ever want to apply this to other databases, it would be the preferred method.)
Alternately, you can just add a decimal point to the divisor, as SQL server will return the more precise data type when doing arithmetic on heterogeneous types:
SELECT 12345/100.0
According to the documentation, the numeric data type is functionally equivalent to the decimal datatype, so there's really no reason to convert between the two. It seems that all you really want to do is divide the value you have by 100:
SELECT 12345/100

Wrong value when casting a float(24)value to float(53) in SQL 2005

I am running this sql code in SQL 2005
declare #a as float(24)
set #a=0.85
select cast ( #a as float(53))
and the result is
0.850000023841858
does anyone know why?
Thanks.
The first seven digits are the default level of precision for a float when in the declaration float(N) the value of N is 24 or less. After that, the digits could show up as pretty much anything when cast to a float of greater precision. That's the 'floating point'.
http://msdn.microsoft.com/en-us/library/ms173773.aspx
The number you see is as close as the computer can get within however many binary digits it has available to use.
If you try and write 1/3 in decimal, but you only have enough space for 8 digits, the closest you can get is 0.33333333. That's still off by a quite a way, but if you had more decimal places you could get more accurate. This is exactly the same probably as the computer faces, but whereas each of your successive digits represents 1/10ths, 1/100ths, 1/1000ths, the computer works in 1/2, 1/4, 1/8, 1/16.