Get the complete number when divide two fields - sql-server

I was trying to round some fields. When I have 59 days, I want to change it to 60.
The problem is that when I use this code, the 59 is changed to 30 because the round it is 1.
select round(1.9666,0)*30, round(59/30,0)*3'
The result of that query is 60 for the first field and 30 for the second one. The problem is that when I've tried:
select 59/30
The result is 1 and I need the entire answer that is 1.9666...
How can I make it?

Because the number you are dividing by is an INT (the data type of the left side is irrelevant), SQL Server will return an INT as the answer.
If you want a number with a decimal place as your result, you'll need to divide by one.
Don't cast to a FLOAT as the answer is probably not what you want (floats are generally not accurate and are 'approximations'):
SELECT 59 / CAST(30 AS FLOAT) -- = 1.96666666666667
CAST the right-hand side of the division to a DECIMAL:
SELECT 59 / CAST(30 AS DECIMAL(10, 2)) -- = 1.96666

SELECT cast(59 AS FLOAT) / cast(30 AS FLOAT)
Because the original figures are whole numbers, SQL presumes you want a whole number output.
To ensure you get one with the decimal places, you need to first change the data type from an integer int to a floating point float.
This is what the CAST command does.
EDIT: Commenter suggests you cast to DECIMAL instead. The principle is the same, but you need to supply more arguments. To cast to a decimal use something like:
cast(59 as DECIMAL(18, 3))
The first argument (the 18) is the total number of figures you want to permit in the decimal. The second argument (the 3) is the number you want after the decimal point.
The suggestion that it's more accurate is correct - as you'll see if you run the SELECT statements in this answer one after the other. But in this particular case, it only makes a tiny difference.

Related

SQL Server Decimal Operation is Not Accurate

When I run this simple operation in SQL server:
Select 800.0 /30.0
I get the value 26.666666, where even if it rounds for 6 digits it should be 26.666667.
How can I get the calculation to be accurate? I tried to search about it online and I found a solution where I cast each operand to a high precision decimal before the operation, but this will not be convenient for me because I have many long complex calculations. think there must be a better solution.
When a using division, in SQL Server, any digits after the resulting scale are truncated, not rounded. For your expression you have a decimal(4,1) and a decimal(3,1), which results in a decimal(10,6):
Precision = p1 - s1 + s2 + max(6, s1 + p2 + 1)
Scale = max(6, s1 + p2 + 1)
As a result, 26.66666666666666~ is truncated to 26.666666.
You can get around this by can increasing the size of the precision and scale, and then CONVERT back to your required precision and scale. For example, increase the precision and scale of the decimal(3,1) to decimal(5,2) and convert back to a decimal(10,6):
SELECT CONVERT(decimal(10,6),800.0 / CONVERT(decimal(5,3),30.0));
This returns 26.666667.
This might helpful:
Use ROUND (Transact-SQL)
SELECT ROUND(800.0 /30.0, 5) AS RoundValue;
Result:
RoundValue
26.666670
I believe it's because SQL Server takes your numbers as decimal values (which are exact e.g., 6.6666 and 6.6667 means exactly those values, not 6 and two-thirds) rather than float values (which can work with approximate numbers).
If you explicity cast/convert it to a float at the start, you should get your calculations running smoothly.
Here's some examples to demonstrate the difference between int, decimal, and float calculations
Dividing 20 by 3
Dividing 20 by 3, then multiplying by 3 again (which mathematically should be 20).
SELECT (20/3) AS int_calc,
(20/3) * 3 AS int_calc_x3,
(CAST(20 AS decimal(10,3)) /3) AS dec_calc,
(CAST(20 AS decimal(10,3)) /3) * 3 AS dec_calc_x3,
(CAST(20 AS float) /3) AS float_calc,
(CAST(20 AS float) /3) * 3 AS float_calc_x3
with the following results
int_calc int_calc_x3 dec_calc dec_calc_x3 float_calc float_calc_x3
6 18 6.666666 19.999998 6.66666666666667 20
In your case, you can use
Select CAST(800.0 AS float) /30.0
which results in 26.6666666666667
Note if you then multiply back by 30, it gets the correct result e.g.,
Select (CAST(800.0 AS float) /30.0) * 30
results in 800. Solutions dealing with decimals will not have this.
Note also that once you have it as a float, then it should stay a float until converted back to a decimal or an int somehow (e.g., saved in a table as an int). So...
SELECT A.Num / 30
FROM (Select ((CAST(800.0 AS float) /30.0) * 30) AS Num) AS A
will still result in 26.6666666666667
This will hopefully help you in your long complex calculations.

Result when subtrahend calls round function

I'm dividing two floats, multiplying it by 100 and then subtracting it by 100. I'm returning a percentage.
My question is: why is the final result a float that isn't rounded when the right part of the subtraction returns a float of 2 digits?
These is one sequence:
/* 1 */
-- Returns 0.956521739130435, which is correct.
select cast(198 as float)/(cast(198 as float) + cast(9 as float)) -- correct
/* 2 */
-- Returns 95.6521739130435, which is correct.
select 100*(cast(198 as float)/(cast(198 as float) + cast(9 as float))) --correct
/* 3 */
-- It's the same as previous one, but with a ROUND
-- Returns 95.65, which is correct.
select round(100*(cast(198 as float)/(cast(198 as float) + cast(9 as float))),2)
/* 4 */
-- Returns 4.34999999999999, should be 100-95.65, but it's not. ROUND is ignored. Why?
select 100-round(100*(cast(198 as float)/(cast(198 as float) + cast(9 as float))),2)
|-------------- This returns 95.65 --------------------------------|
Another sequence:
/* 1 */
-- Returns 0.956521739130435, which is correct.
select cast(198 as float)/(cast(198 as float) + cast(9 as float))
/* 2 */
-- Returns 0.9565, which is correct.
select round(cast(198 as float)/(cast(198 as float) + cast(9 as float)), 4)
/* 3 */
-- Returns 95.65, which is correct.
select 100*round(cast(198 as float)/(cast(198 as float) + cast(9 as float)), 4)
/* 4 */
-- Returns 4.34999999999999, should be 100-95.65, but it's not. ROUND is ignored. Why?
select 100-(100*round(cast(198 as float)/(cast(198 as float) + cast(9 as float)), 4))
|-------------------- This returns 95.65 --------------------------------|
I'm just curious as to why this happens, although it can easily be fixed with one ROUND at the beginning:
select round(100-(100*(cast(198 as float)/(cast(198 as float) + cast(9 as float)))), 2)
The reason I ask is because it's not something that can be easily reproduced. I tried reproducing it, and out of 2,000 times, it only occurred 12 times. That's less than 1%, but with floats with repetitive numbers after the 2nd decimal (ie. 3.47999999999), which makes sense:
declare #rand int = 1
While(#rand <= 2000)
begin
select 100-round(100*(cast(abs(checksum(NewId()) % 1500) as float)/(cast(abs(checksum(NewId()) % 1500) as float) + cast(abs(checksum(NewId()) % 1500) as float))),2)
set #rand = #rand + 1
end
I guess my other question is: what type is the sql editor returning when it returns 95.65 with select round(100*(cast(198 as float)/(cast(198 as float) + cast(9 as float))),2)?
To expand on Jeroen's comment:
SQL Server's FLOAT type is a double-precision floating-point value. As with (most) floating point formats, the value is stored in binary. Just as the number 1/3 cannot be represented with a finite number of digits after the decimal, the number 95.65 cannot be represented with a finite number of bits. The closest value to 95.65 that can be stored in a FLOAT has the exact value:
95.650000000000005684341886080801486968994140625
If you subtract that number from 100, you get exactly:
4.349999999999994315658113919198513031005859375
When displayed, this is rounded to 15 significant digits, and the value printed is:
4.34999999999999
As discussed, you can solve this problem by using DECIMAL type instead of FLOAT.
There are many resources available on StackOverflow and elsewhere if you'd like to learn more about floating-point math.
-- EDIT --
I'm going to use parenthesis notation for repeating decimals. When I write
0.(3)
that means
0.333333333333333333333333333... and so on forever.
Let's start at the beginning. 168 can be stored in a float. 168+9 is 177. That can be stored in a float. If you divide 168 by 177 the mathematically correct answer is:
0.95(6521739130434782608695)
But this value cannot be stored in a float. The closest value that can be stored in a float is:
0.9565217391304348115710354250040836632251739501953125
Take that number and multiply by 100 , the mathematically correct answer is:
95.65217391304348115710354250040836632251739501953125
Since you multiplied a float by 100, you get a float, and that number cannot be stored in a float, so the closest possible value is:
95.6521739130434838216388016007840633392333984375
You ask that this float be rounded to 2 digits after the decimal. The mathematically correct answer is:
95.65
But since you asked to round a float, the answer is also a float, and that value cannot be stored in a float. The closest possible value is:
95.650000000000005684341886080801486968994140625
You asked to subtract that from 100. The mathematically correct value is:
4.349999999999994315658113919198513031005859375
As it happens, that value can be stored in a float. So that's the value that's being selected.
When converting this number to a string, SQL Server rounds the result to 15 significant digits. So that number, when printed, appears as:
4.34999999999999
When you ran the same calculation on your Java console, the exact same calculations were performed, but when the value was printed, Java rounded to 16 significant digits:
4.349999999999994
-- Another EDIT --
Why can't 96.65 be stored exactly in a float? The float type stores numbers in binary format. If you want to express 96.65 in binary, the mathematically exact value is:
1011111.1010011001100110011001100110011001100110011001(1001)
You can see the pattern. Just as 1/3 is represented as an infinite repeating value in decimal, this value has an infinite repeating value in binary. You can see the pattern (1001) being repeated over and over.
A float can only hold 53 significant bits. And so this is rounded to:
1011111.1010011001100110011001100110011001100110011010
If you convert that number back to decimal, you get the exact value:
95.650000000000005684341886080801486968994140625
-- Yet Another Edit --
You ask what happens when you call Round again on the result.
We started with the number:
4.349999999999994315658113919198513031005859375
You ask that this be rounded to 2 places. The mathematically correct answer is:
4.35
Since you are rounding a float, this result must also be a float. Express this value in binary. The mathematically correct answer is:
100.0101100110011001100110011001100110011001100110011001(1001)
Again, this is a repeating binary value. But float can't store an infinite number of bits. The value is rounded to 53 significant bits. The result is:
100.0101100110011001100110011001100110011001100110011
If you convert this to decimal, the exact value is:
4.3499999999999996447286321199499070644378662109375
That is the value you selected. Now SQL Server needs to print that on the screen. As before, it is rounded to 15 significant digits. The result is:
4.35000000000000
It removes the trailing zeros, and the result you see on the screen is:
4.35
The last round did nothing magic. The answer is still stored as a float, and the answer is still not an exact value. As it happens SQL Server chooses to round values to 15 significant digits when printing a float. In this case, that rounded value happened to match the exact value you were expecting.
If values were rounded to 14 places when printing them, the original query would have appeared to have the value you expected.
If values were rounded to 16 places, then the result of the final round would be shown as
4.3499999999999996

How to remove two digits after decimal in SQL Server

I have a number with two digits after the decimal point; I try to cast this number using CAST for example:
Select SUM(CAST((123345.56) as decimal(28, 2))) * 100 AS AMOUNT
I get the following result
12334556.00
but I want get only 12334556 without displaying two digits after the decimal point.
Thank you.
It is not clear what you really want to do, but
SELECT SUM(CAST((123345.56 * 100.0) AS DECIMAL(28,0))) AS AMOUNT
gives 12334556.
When you use a number in the format 1234.56 it is a decimal literal in SQL Server: see "decimal constants" in Constants (Transact-SQL).
Integer data type is whole numbers, so cast your calculated value as int.
Select SUM(CAST((123345.56 * 100) as int)) AS AMOUNT

Why does a FLOAT give me a more accurate result than a DECIMAL?

I am looking for a division result that is extremely accurate.
This SQL returns the following results:
SELECT (CAST(297282.26 AS DECIMAL(38, 30)) / CAST(495470.44 AS DECIMAL(38, 30))) AS ResultDecimal
SELECT (CAST(297282.26 AS FLOAT) / CAST(495470.44 AS FLOAT)) AS ResultFloat
Here is the accurate result from WolframAlpha:
http://www.wolframalpha.com/input/?i=297282.26%2F495470.44
I was under the impression that DECIMAL would be more accurate than FLOAT:
"Because of the approximate nature of the float and real data types, do not use these data types when exact numeric behavior is required, such as in financial applications, in operations involving rounding, or in equality checks. Instead, use the integer, decimal, money, or smallmoney data types."
https://technet.microsoft.com/en-us/library/ms187912(v=sql.105).aspx
Why does the FLOAT calculation give me a result more accurate than when using DECIMAL?
I found the best precision to be when you use:
SELECT (CAST(297282.26 AS DECIMAL(15, 9)) / CAST(495470.44 AS DECIMAL(24, 2))) AS ResultDecimal
This gives a result of
0.599999991926864496699338915153
I think the actual value (to 100 digits) is:
0.5999999919268644966993389151530412187657451370862810705720405842980259326873264124495499670979362562...
Please bear in mind SQL Server defines the maximum precision and scale for division as:
max precision = (p1 - s1 + s2) + MAX(6, s1 + p2 + 1) -- up to 38
max scale = MAX(6, s1 + p2 + 1)
Where p1 & p2 are the precision of the two numbers and s1 & s2 are the scale of the numbers.
In this case the maximum precision is (15-9+2) + MAX(6, 9+24+1) = 8 + 34 = 42.
However SQL Server only allows a maximum precision of 38.
The maximum scale = MAX(6, 9+24+1) = 34
Hopefully you already understand that just because the FLOAT version presents more numbers after the decimal point, doesn't necessarily mean that those are the true numbers. This is about precision, not accuracy.
It is the CAST function itself that causes this loss of precision, not the difference between the FLOAT and DECIMAL data types.
To demonstrate this, compare your previous results to the result of this:
SELECT 297282.26 / 495470.44 AS ResultNoCast
In my version of the query, the presence of a decimal point in the literal numbers tells SQL Server to treat the values as DECIMAL datatype, with appropriate length and precision as determined by the server. The result is more precise than when you CAST explicitly to DECIMAL.
A clue to the reason for this can be found hidden in the official documentation of the CAST function, under Truncating and Rounding Results:
When you convert data types that differ in decimal places, sometimes the result value is truncated and at other times it is rounded. The following table shows the behavior.
From | To | Behavior
numeric | numeric | Round
So the fact that each separate literal value is treated as a NUMERIC (same thing as DECIMAL) on the way in, and is being casted to NUMERIC, causes rounding.
Anticipating your next question a little, if you want a more precise result from the NUMERIC/DECIMAL datatype, you just need to tell SQL Server that each component of the calculation is more precise:
SELECT 297282.26000000 / 495470.44000000 AS ResultSuperPrecise
This appears (from experimentation) to be the most precise I can get: either adding or removing a 0 from either the numerator or denominator makes the result less precise. I'm at a loss to explain why that is, because the result is only 23 digits to the right of the decimal point.
It doesn't give you a more accurate result. I say that because the value is an approximate and not all values will be available to stored in a float. On the other side of that coin though is that float has the possibility of a lot more precision. The maximum precision of a decimal/numeric is 38. https://msdn.microsoft.com/en-us/library/ms187746.aspx
When you look at float though the maximum precision is 53. https://msdn.microsoft.com/en-us/library/ms173773.aspx
Okay, here is what I think is going on.
#philosophicles - I think you are right in that the CAST is causing the problem, but not because I am trying to "convert data types that differ in decimal places".
When I execute the following statement
SELECT CAST((297282.26 / 495470.44) AS DECIMAL(38, 30)) AS ResultDecimal
The accurate result for the calculation is
This has way more than 30 digits after the decimal point, and my data type has scale set to 30. So the CAST rounds the value, then just adds zeros to the end until there are 30 digits. We end up with this:
So the interesting thing is how does the CAST determine up to how many decimals to round or truncate the output? I am not sure, but as #philosophicles pointed out, the scale of the input effects the rounding applied on the output.
SELECT CAST(((297282.26/10000) / (495470.44/10000)) AS DECIMAL(38, 30)) AS ResultDecimal
Thoughts?
Also interesting:
However, in simple terms, precision is lost when the input scales are
high because the result scales need to be dropped to 38 with a
matching precision drop.
https://dba.stackexchange.com/questions/41743/automatic-decimal-rounding-issue
The precision and scale of the numeric data types besides decimal are fixed.
https://dba.stackexchange.com/questions/41743/automatic-decimal-rounding-issue

SQL Server decimal scale length - can be or has to be?

I have really simply question about DECIMAL (and maybe NUMERIC) type in SQL Server 2008 R2.
MSDN said:
(scale)
The maximum number of decimal digits that can be stored to the right of the decimal point. Scale must be a value from 0 through p.
I understand this following way:
if I have DECIMAL(10, 5) - I am able to store 12345.12345 or 12345678.91.
if I have DECIMAL(5, 5) - I can have 12345 or 1234.5 or 1.2345, etc...
Is it clear?
But I got this error message:
SELECT CAST(2.8514 AS DECIMAL(5,5))
Arithmetic overflow error converting numeric to data type numeric.
I thought 5,5 means I can have up to 5 digits and up to 5 CAN BE right of the decimal point.
As I tried:
SELECT CAST(12.851 AS DECIMAL(6,5)) - overflows too
however
SELECT CAST(1.23456 AS DECIMAL(6,5)) - is OK.
So what's the truth?
DECIMAL(a,b) says that I can have up to a digits and JUST b of them are right to the decimal point (and there rest a-b to the left to the dec. point)?
I'm really confused about statement in doc which is copied everywhere. Please take a while and explain me this simple thing.
Lot of thanks!
The easiest way to think of it (for me) is that precision is the total number of digits, of which scale is the number of digits to the right of the decimal point. So DECIMAL(p,s) means p-s digits to the left of the point, and s digits to the right of the point.
That explains all the conversion errors you're seeing: the 2.8514 cannot be decimal(5,5) because p-s = 0; 12.851 cannot be decimal(6,5) because p-s = 1 and so on.

Resources