Implicit conversions and rounding - sql-server

Just come across an interesting one:
declare #test as int
set #test = 47
select #test * 4.333
returns 203.651
declare #test as int
set #test = 47
declare #out as int
set #out = (select #test * 4.333)
select #out
returns 203
declare #test as int
set #test = 47
declare #out as int
set #out = round((select #test * 4.333),0)
select #out
returns 204
Now I know why it does this. Its because there is an implicit conversion from decimal to int, therefore the decimal places need chopped off (hence 203), whereas if I round prior to the implicit conversion I get 204.
My question is why when SQL Server does an implicit conversion is it not also rounding? I know if I had a big number, and it needed stored in a small place, the first thing I'd do would be to round it so as to be as close to the original number as possible.
It just doesn't seem intuitive to me.

This got me reading and the answer seems to be distinctly unsatisfying, The earliest SQL reference I've been able to find (ANSI 92 available here) in section 4.4.1 Characteristics of numbers states that
Whenever an exact or approximate numeric value is assigned to a
data item or parameter representing an exact numeric value,
an approximation of its value that preserves leading significant
digits after rounding or truncating is represented in the data
type of the target. The value is converted to have the precision and
scale of the target. The choice of whether to truncate or round
is implementation-defined.
Which leaves it up to Microsoft which of the two they chose to implement for T-SQL and I assume for the sake of simplicity they chose truncation. From the wikipedia article on rounding it seems that this wasn't an uncommon decision back in the day.
It's interesting to note that, according to the documentation I found, only conversions to integers cause truncation, the others cause rounding. Although for some bizarre reason the conversion from money to integer appears to buck the trend as it's allowed to round.
From To Behaviour
numeric numeric Round
numeric int Truncate
numeric money Round
money int Round
money numeric Round
float int Truncate
float numeric Round
float datetime Round
datetime int Round
Table from here.

Related

Does the decimal scale value replace rounding in SQL Server? [duplicate]

Just come across an interesting one:
declare #test as int
set #test = 47
select #test * 4.333
returns 203.651
declare #test as int
set #test = 47
declare #out as int
set #out = (select #test * 4.333)
select #out
returns 203
declare #test as int
set #test = 47
declare #out as int
set #out = round((select #test * 4.333),0)
select #out
returns 204
Now I know why it does this. Its because there is an implicit conversion from decimal to int, therefore the decimal places need chopped off (hence 203), whereas if I round prior to the implicit conversion I get 204.
My question is why when SQL Server does an implicit conversion is it not also rounding? I know if I had a big number, and it needed stored in a small place, the first thing I'd do would be to round it so as to be as close to the original number as possible.
It just doesn't seem intuitive to me.
This got me reading and the answer seems to be distinctly unsatisfying, The earliest SQL reference I've been able to find (ANSI 92 available here) in section 4.4.1 Characteristics of numbers states that
Whenever an exact or approximate numeric value is assigned to a
data item or parameter representing an exact numeric value,
an approximation of its value that preserves leading significant
digits after rounding or truncating is represented in the data
type of the target. The value is converted to have the precision and
scale of the target. The choice of whether to truncate or round
is implementation-defined.
Which leaves it up to Microsoft which of the two they chose to implement for T-SQL and I assume for the sake of simplicity they chose truncation. From the wikipedia article on rounding it seems that this wasn't an uncommon decision back in the day.
It's interesting to note that, according to the documentation I found, only conversions to integers cause truncation, the others cause rounding. Although for some bizarre reason the conversion from money to integer appears to buck the trend as it's allowed to round.
From To Behaviour
numeric numeric Round
numeric int Truncate
numeric money Round
money int Round
money numeric Round
float int Truncate
float numeric Round
float datetime Round
datetime int Round
Table from here.

Weired Behavior of Round function in MSSQL Database for Real column only

I found weird or strange behavior of Round function in MSSQL for real column type. I have tested this issue in Azure SQL DB and SQL Server 2012
Why #number=201604.125 Return 201604.1 ?
Why round(1.12345,10) Return 1.1234500408 ?
-- For Float column it working as expected
-- Declare #number as float,#number1 as float;
Declare #number as real,#number1 as real;
set #number=201604.125;
set #number1=1.12345;
select #number as Realcolumn_Original
,round(#number,2) as Realcolumn_ROUND_2
,round(#number,3) as Realcolumn_ROUND_3
, #number1 as Realcolumn1_Original
,round(#number1,6) as Realcolumn1_ROUND_6
,round(#number1,7) as Realcolumn1_ROUND_7
,round(#number1,8) as Realcolumn1_ROUND_8
,round(#number1,9) as Realcolumn1_ROUND_9
,round(#number1,10) as Realcolumn1_ROUND_10
Output for real column type
I suspect what you are asking here is why does:
DECLARE #n real = 201604.125;
SELECT #n;
Return 201604.1?
First point of call for things like this should be the documentation: Let's start with float and real (Transact-SQL). Firstly we note that:
The ISO synonym for real is float(24).
If we then look further down:
float [ (n) ] Where n is the number of bits that are used to store the
mantissa of the float number in scientific notation and, therefore,
dictates the precision and storage size. If n is specified, it must be
a value between 1 and 53. The default value of n is 53. n value
Precision Storage size
1-24 7 digits 4 bytes
So, now we know that a real (aka a float(24)) has precision of 7. 201604.125 has a precision of 9, that's 2 too many; so off come that 2 and 5 in the return value.
Now, ROUND (Transact-SQL). That states:
Returns a numeric value, rounded to the specified length or precision.
When using real/float those digits aren't actually lost, as such, due to the floating point. When you use ROUND, you are specifically stating "I want this many decimal places". This is why you can then see the .13 and the .125, as you have specifically asked for those. When you just returned the value of #number it had a precision of 7, due to being a real, so 201604.1 was the value returned.

SQL Server stored procedure conversion decimal point without rounding to two decimal

Decimal point conversion without rounding to two decimal
My variable is of datatype varchar, so I have to convert it to numeric. But what the thing is my output value is 0.0012499987 and I want the output as 1.24 i.e. without rounding the value.
This is my code
Set #SQLQuery = #SQLQuery + 'CAST((ISNULL(CAST(DI.Coupon AS NUMERIC(18,4)),0) * 100) AS Varchar(50)) AS Coupon,
Here I have to multiply with 100 don't remove that; di.coupon is of type varchar. Keep it in your mind
And the result value also I want as a varchar.
Please someone help me
Sample input / output
0.013923 1.39
You can use CAST
EDIT: I added an ISNULL
DECLARE #N
SET #N = '0.013923'
SELECT CAST(CAST(CAST(ISNULL (#N, 0) AS DECIMAL(38,18)) * 100 AS DECIMAL(18,2)) AS VARCHAR (50))
Probably the easiest is to get the substring of the original column and cast that to numeric. Then it will drop the remaining digits.
In SQL Server, LEFT(column, 4) will do what you want.
But as #HABO pointed out, the in-built function Round() will accept a parameter that truncates the decimal value.
I have got the answer for this. This might help for some people
cast(left(('00122.45678')*100,instr(('00122.45678')* 100,'.','1')+3)as varchar) as stb
Output:
122.456
If you want for 2 decimal without round of then you can add like +2 instead of +3

TSQL number rounding issue

I have a piece of code:
IF OBJECT_ID(N'dbo.rounding_testing') IS NOT NULL
DROP FUNCTION dbo.rounding_testing;
GO
CREATE FUNCTION dbo.rounding_testing
(
#value FLOAT,
#digit INT
)
RETURNS FLOAT
BEGIN
DECLARE
#factor FLOAT,
#result FLOAT;
SELECT #factor = POWER(10, #digit);
SELECT #result = FLOOR(#value * #factor + 0.4);
RETURN #result;
END;
GO
SELECT dbo.rounding_testing(5.7456, 3);
SELECT FLOOR(5.7456 * 1000 + 0.4);
The results are:
5745
5746
I'm expecting two 5746. I tried to debug the function and found some interesting behavior. Below are some testing I did in the Immediate Window when debugging.
#factor
1.000000000000000e+003
#result
5.745000000000000e+003
#value
5.745600000000000e+000
#value*#factor
5745.6
#value*#factor+0.4
5746
floor(#value*#factor+0.4)
5745
floor(5746)
5746
Can anyone help to explain the result? Especially these three lines:
#value*#factor+0.4
5746
floor(#value*#factor+0.4)
5745
floor(5746)
5746
In the expression FLOOR(5.7456 * 1000 + 0.4);, the part between parentheses is evaluated first. For constants the data types are inferred based on the notation; for 5.7456 that is decimal(5,4); 1000 is an int; and 0.4 is decimal(1,1). The inferred data type for 5.7456 * 1000 is then decimal(10,4); and for the full expression it is decimal(11,4). These are all exact numeric data types so you will not experience any rounding; the end result is 5746.0000 exactly. The FLOOR function trims the fraction and converts to decimal(11,0), returning 5746.
In the user-defined function, you store input parameters and intermediate results in float data type (floating point data). This data type is intended to be used for approximate data, such as measurements, where the data you read from the intstrument is already an approximation. I have learned in high school to read as many digits as I can, but treat the last one as insignificant - I had to keep it in all computations, but round the final result to the number of significant digits based on the accuracy of my measurements. The rounding ensures that inaccuracies in the last digits will not affect the end result.
Floating point data types should be treated in the same way.
Internally, floating point digits are represented in a base-2 number system. This means that there are numbers that have an exact representation in our commonly used base-10 system (such as 5.7456), but a never ending fractional part in base-2. (Similar to how for instance one third, which can be represented exactly in base-3, has a never ending fractional part in base-10: 0.33333333333(etc)). The number of base-2 digits used for storage of a float number is finite, so it has to be cut off at the end - which results in it being rounded either up or down by a tiny fraction. You can see this if you run:
DECLARE #a float = 5.7456;
SELECT CAST(#a AS decimal(19,16));
In this case, the effect of cutting off after a lot of base-2 digits is that the value stored is 0.0000000000000004 less than the decimal value you put in. That small difference turns into a huge effect because of the FLOOR function, which does exactly what it should do: round down to the nearest integer.
(I've seen a lot of people call this an error. It is not. It is intended and documented behavior. And the precision loss here is neither worse nor better than the precision loss you get when you store a third in a DECIMAL(7,6); it is just a bit less obvious because we have all grown up being used to working in base-10)
You issue can be fixed by changing float to real
IF OBJECT_ID(N'dbo.rounding_testing') IS NOT NULL
DROP FUNCTION dbo.rounding_testing;
GO
CREATE FUNCTION dbo.rounding_testing
(
#value REAL,
#digit INT
)
RETURNS REAL
BEGIN
DECLARE
#factor REAL,
#result REAL;
SELECT #factor = POWER(10, #digit);
SELECT #result = FLOOR(#value * #factor + 0.4);
RETURN #result;
END;
GO
SELECT dbo.rounding_testing(5.7456, 3);
SELECT FLOOR(5.7456 * 1000 + 0.4);
sum(convert(int,<your column) * .01) as 'Decimal Amount'
Convert column to integer, then multiply by .01. Sum converted field, if desired.

T-Sql numeric variables error conversion

It is really strange how auto convert between numeric data behaves in T-Sql
Declare #fdays as float(12)
Declare #mAmount As Money
Declare #fDaysi as float(12)
Set #fdays =3
Set #fdaysi =1
Set #mAmount=527228.52
Set #mAmount = #fdaysi * #mAmount/#fDays
Select #mAmount, 527228.52/3
The result of this computation is
175742.8281 175742.840000
Does this occur because money and float are not actually the same kind of numeric data? Float is Approximate Numeric and Money is Exact Numeric
Money and Decimal are fixed numeric datatypes while Float is an
approximate numeric datatype. Results of mathematical operations on
floating point numbers can seem unpredictable, especially when
rounding is involved. Be sure you understand the significance of the
difference before you use Float!
Also, Money doesn't provide any advantages over Decimal. If fractional
units up to 5 decimal places are not valid in your currency or
database schema, just use Decimal with the appropriate precision and
scale.
ref link : http://www.sqlservercentral.com/Forums/Topic1408159-391-1.aspx
Should you choose the MONEY or DECIMAL(x,y) datatypes in SQL Server?
https://dba.stackexchange.com/questions/12916/datatypes-in-sql-server-difference-between-similar-dataypes-numeric-money
float [ (n) ]
Where n is the number of bits that are used to store the mantissa of the float number in scientific notation and, therefore, dictates the precision and storage size. If n is specified, it must be a value between 1 and 53. The default value of n is 53.
When n in 1-24 then precision is 7 digits.
When n in 25-53 then precision is 15 digits.
So in your example precision is 7 digits, thus first part #fdaysi * #mAmount
rounds result to 7 digits 527228.5. The second part returns 527228.5/3=175742.828 and casting 175742.828 to Money results in 175742.8281. So FLOAT and REAL are approximate data types and sometimes you get such surprises.
DECLARE #f AS FLOAT = '29545428.022495';
SELECT CAST(#f AS NUMERIC(28, 14)) AS value;
The result of this is 29545428.02249500200000 with just a casting.

Resources