Why is my float displayed as 0 in postgreSQL - sql

One of my columns process_size holds a value 800089856 in bytes.
My SQL query says
select ((process_size*11/(1024*1024*1024))*100)/(4*3600) as Avg_wk_sum from instances where wk_id = 2
But instead of a float value it shows 0.
I even tried explicit cast
select ((process_size*11/(1024*1024*1024))*100)/(4*3600) :: float as Avg_wk_sum from instances where wk_id = 2
What am I doing wrong? How can I get the float value?

All values in your statement are integers, so the actual division/multiplication is carried out using integers - which yields 0. You then cast the result (0) to a float which doesn't change anything.
You should cast process_size to a float, then all subsequent operations are carried out using floats.
select ((process_size::float*11/(1024*1024*1024))*100)/(4*3600)
However if you care about precise results, you should stay away from approximate data types like float and use numeric instead.

Related

Len function on Float in SQLServer gives wrong length

I am using the below query in SQL Server.
declare #dt float
set #dt = 1079938.05
select #dt AS Val,Convert(nvarchar(20),#dt) AS NVal, len(#dt) AS Len
Its output is
Val NVal Len
1079938.05 1.07994e+006 12
My questions are:
'Val' column shows right value.
'NVal' column shows strange value please explain us why it shows like this?
'Len' shows length and its actual length is 10 but it shows us 12. Please explain why it shows 12 instead of 10.
A float in sql server can be 4 or 8 byte. Find details.
LEN() is a function to measure the lenght of a string. So you want to measure the length of the string representation of the value, not the value itself.
The shown display value 1.07994e+006 is scientific notation and has 12 characters. Nothing wrong here.
Your call Convert(nvarchar(20),#dt) calls the CONVERT()-function with the defaul for FLOAT and REAL(Details and other formats here), which is scientific for numbers larger than 6 digits. The same happens implicitly when you call 'len(#dt)'. As the input of LEN() must be a string, the value is converted and then passed to the function.
What you can do:
You might think about a conversion to DECIMAL...
Another choice was first to use STR()-function together with RTRIM().
One more choice was FORMAT()-function (SQL Server 2012+)
.
Anyway you have to consider, that the text you see is not the real value.
LEN() works on [N]VARCHAR(), thus you're running into an implicit conversion from FLOAT to VARCHAR
see this: https://social.msdn.microsoft.com/Forums/sqlserver/en-US/a4ea2bc1-6f2f-4992-8132-f824fe4ffce0/length-of-float-values-in-ms-sql-server-gives-wrong-result?forum=transactsql
That means that LEN converts the value to VARCHAR before it actually calculates its length. That's because the length you get coincides with the length of your NVarChar value 1.07994e+006.
First of all: Don't use approximate data types when not forced to. A FLOAT is just an approximation, e.g. a simple value like 0.123 may be stored as 0.1230000000001 for instance. Use a precise type such as DECIMAL instead.
When converting a number to a string, you should usually specify a format as in format(#dt, '#,###,##0.00'). You don't do so, so it's up to the system what format to use. It uses a scientific notation 1.07994e+006 translating to 1.079940 x 10^6, which is approximately your number.
check:-
select #dt AS Val,Convert(nvarchar(20),#dt) AS NVal, len(CAST(CAST(#dt AS DECIMAL(20)) AS VARCHAR(20))) AS Len

How Can I Get An Exact Character Representation of a Float in SQL Server?

We are doing some validation of data which has been migrated from one SQL Server to another SQL Server. One of the things that we are validating is that some numeric data has been transferred properly. The numeric data is stored as a float datatype in the new system.
We are aware that there are a number of issues with float datatypes, that exact numeric accuracy is not guaranteed, and that one cannot use exact equality comparisons with float data. We don't have control over the database schemas nor data typing and those are separate issues.
What we are trying to do in this specific case is verify that some ratio values were transferred properly. One of the specific data validation rules is that all ratios should be transferred with no more than 4 digits to the right of the decimal point.
So, for example, valid ratios would look like:
.7542
1.5423
Invalid ratios would be:
.12399794301
12.1209377
What we would like to do is count the number of digits to the right of the decimal point and find all cases where the float values have more than four digits to the right of it. We've been using the SUBSTRING, LEN, STR, and a couple of other functions to achieve this, and I am sure it would work if we had numeric fields typed as decimal which we were casting to char.
However, what we have found when attempting to convert a float to a char value is that SQL Server seems to always convert to decimal in between. For example, the field in question shows this value when queried in SQL Server Enterprise Manager:
1.4667
Attempting to convert to a string using the recommended function for SQL Server:
LTRIM(RTRIM(STR(field_name, 22, 17)))
Returns this value:
1.4666999999999999
The value which I would expect if SQL Server were directly converting from float to char (which we could then trim trailing zeroes from):
1.4667000000000000
Is there any way in SQL Server to convert directly from a float to a char without going through what appears to be an intermediate conversion to decimal along the way? We also tried the CAST and CONVERT functions and received similar results to the STR function.
SQL Server Version involved: SQL Server 2012 SP2
Thank you.
Your validation rule seems to be misguided.
An SQL Server FLOAT, or FLOAT(53), is stored internally as a 64-bit floating-point number according to the IEEE 754 standard, with 53 bits of mantissa ("value") plus an exponent. Those 53 binary digits correspond to approximately 15 decimal digits.
Floating-point numbers have limited precision, which does not mean that they are "fuzzy" or inexact in themselves, but that not all numbers can be exactly represented, and instead have to be represented using another number.
For example, there is no exact representation for your 1.4667, and it will instead be stored as a binary floating-point number that (exactly) corresponds to the decimal number 1.466699999999999892708046900224871933460235595703125. Correctly rounded to 16 decimal places, that is 1.4666999999999999, which is precisely what you got.
Since the "exact character representation of the float value that is in SQL Server" is 1.466699999999999892708046900224871933460235595703125, the validation rule of "no more than 4 digits to the right of the decimal point" is clearly flawed, at least if you apply it to the "exact character representation".
What you might be able to do, however, is to round the stored number to fewer decimal places, so that the small error at the end of the decimals is hidden. Converting to a character representation rounded to 15 instead of 16 places (remember those "15 decimal digits" mentioned at the beginning?) will give you 1.466700000000000, and then you can check that all decimals after the first four are zeroes.
You can try using cast to varchar.
select case when
len(
substring(cast(col as varchar(100))
,charindex('.',cast(col as varchar(100)))+1
,len(cast(col as varchar(100)))
)
) = 4
then 'true' else 'false' end
from tablename
where charindex('.',cast(col as varchar(100))) > 0
For this particular number, don't use STR(), and use a convert or cast to varchar. But, in general, you will always have precision issues when storing in float... it's the nature of the storage of that datatype. The best you can do is normalize to a NUMERIC type and compare with threshold ranges (+/- .0001, for example). See the following for a breakdown of how the different conversions work:
declare #float float = 1.4667
select #float,
convert(numeric(18,4), #float),
convert(nvarchar(20), #float),
convert(nvarchar(20), convert(numeric(18,4), #float)),
str(#float, 22, 17),
str(convert(numeric(18,4), #float)),
convert(nvarchar(20), convert(numeric(18,4), #float))
Instead of casting to a VarChar you might try this: cast to a decimal with 4 fractional digits and check if it's the same value as before.
case when field_name <> convert(numeric(38,4), field_name)
then 1
else 0
end
The issue you have here is that float is an approximate number data type with an accuracy of about seven digits. That means it approaches the value while using less storage than a decimal / numeric. That's why you don't use float for values that require exact precision.
Check this example:
DECLARE #t TABLE (
col FLOAT
)
INSERT into #t (col)
VALUES (1.4666999999999999)
,(1.4667)
,(1.12399794301)
,(12.1209377);
SELECT col
, CONVERT(NVARCHAR(MAX),col) AS chr
, CAST(col as VARBINARY) AS bin
, LTRIM(RTRIM(STR(col, 22, 17))) AS rec
FROM #t
As you see the float 1.4666999999999999 binary equals 1.4667. For your stated needs I think this query would fit:
SELECT col
, RIGHT(CONVERT(NVARCHAR(MAX),col), LEN(CONVERT(NVARCHAR(MAX),col)) - CHARINDEX('.',CONVERT(NVARCHAR(MAX),col))) AS prec
from #t

SQL query defining Column type as float with 3 decimal numbers

This is a simple example of what is not working for me:
CREATE TABLE Vertex(
PointID CHARACTER(15) PRIMARY KEY,
Height FLOAT(6,3)
);
After input like this:
INSERT INTO Vertex values("Tryout 1",555.22689562);
I expect the Height to be saved with the value: 555.227
However it is not the case for me, I keep finding the whole number being saved.
Could you point me to an alternate of how to define a column type and contain the format xxxxxx.xxx
Change float to decimal and it will work.
Height decimal(6,3)
Float is an approximate number data type. Using float may cause loss of precision, and using float data type for equality may not work all times.
Decimal data type is fixed precision data type. For using decimal data types, you will need to convert to the data type like convert(decimal(6,3), <number>).
In case of insert query, it is done implicitly though.

SQL server 'like' against a float field produces inconsistent results

I am using LIKE to return matching numeric results against a float field. It seems that once there are more than 4 digits to the left of the decimal, values that match my search item on the right side of the decimal are not returned. Here's an example illustrating the situation:
CREATE TABLE number_like_test (
num [FLOAT] NULL
)
INSERT INTO number_like_test (num) VALUES (1234.56)
INSERT INTO number_like_test (num) VALUES (3457.68)
INSERT INTO number_like_test (num) VALUES (13457.68)
INSERT INTO number_like_test (num) VALUES (1234.76)
INSERT INTO number_like_test (num) VALUES (23456.78)
SELECT num FROM number_like_test
WHERE num LIKE '%68%'
That query does not return the record with the value of 12357.68, but it does return the record with the value of 3457.68. Also running the query with 78 instead of 68 does not return the 23456.78 record, but using 76 returns the 1234.76 record.
So to get to the question: why having a larger number causes these results to change? How can I change my query to get the expected results?
The like operator requires a string as a left-hand value. According to the documentation, a conversion from float to varchar can use several styles:
Value Output
0 (default) A maximum of 6 digits. Use in scientific notation, when appropriate.
1 Always 8 digits. Always use in scientific notation.
2 Always 16 digits. Always use in scientific notation.
The default style will work fine for the six digits in 3457.68, but not for the seven digits in 13457.68. To use 16 digits instead of 6, you could use convert and specify style 2. Style 2 represents a number like 3.457680000000000e+003. But that wouldn't work for the first two digits, and you get an unexpected +003 exponent for free.
The best approach is probably a conversion from float to decimal. That conversion allows you to specify the scale and precision. Using scale 20 and precision 10, the float is represented as 3457.6800000000:
where convert(decimal(20,10), num) like '%68%'
When you are comparing number with LIKE it is implicitly converted to string and then matched
The problem here is that float number is not precise and when it is converted you can get
13457.679999999999999 instead of 13457.68
So to avid this explicitly format number in appropriate format(not sure how to do this in sql server, but it will be something like)
SELECT num FROM number_like_test
WHERE Format("0.##",num) LIKE '%68%'
The conversion to string is rounding your values. Both CONVERT and CAST have the same behavior.
SELECT cast(num as nvarchar(50)) as s
FROM number_like_test
Or
SELECT convert(nvarchar(50), num) as s
FROM number_like_test
provide the results:
1234.56
3457.68
13457.7
1234.76
23456.8
You'll have to use the STR function and correct format parameters to try to get your results. For example,
SELECT STR(num, 10, 2) as s
FROM number_like_test
gives:
1234.56
3457.68
13457.68
1234.76
23456.78
Pretty well solved already, but you only need to CAST once, not twice like the other answer suggests, LIKE takes care of the string conversion:
SELECT *
FROM number_like_test
WHERE CAST(num AS DECIMAL(12,6)) LIKE '%68%'
And here's a SQL Fiddle showing the rounding behavior: SQL Fiddle
It's probably because a FLOAT data type represents a floating point number which is an approximation of the number and should not be relied on for exact comparisons.
If you need to do a search that includes the float value you would need to either store it in a decimal data type (which will hold the exact number) or convert it to a varchar using something like the STR() function

TSQL - make a literal float value

I understand the host of issues in comparing floats, and lament their use in this case - but I'm not the table author and have only a small hurdle to climb...
Someone has decided to use floats as you'd expect GUIDs to be used. I need to retrieve all the records with a specific float value.
sp_help MyTable
-- Column_name Type Computed Length Prec
-- RandomGrouping float no 8 53
Here's my naive attempt:
--yields no results
SELECT RandomGrouping
FROM MyTable
WHERE RandomGrouping = 0.867153569942739
And here's an approximately working attempt:
--yields 2 records
SELECT RandomGrouping
FROM MyTable
WHERE RandomGrouping BETWEEN 0.867153569942739 - 0.00000001
AND 0.867153569942739 + 0.00000001
-- 0.867153569942739
-- 0.867153569942739
In my naive attempt, is that literal a floating point literal? Or is it really a decimal literal that gets converted later?
If my literal is not a floating point literal, what is the syntax for making a floating point literal?
EDIT: Another possibility has occurred to me... it may be that a more precise number than is displayed is stored in this column. It may be impossible to create a literal that represents this number. I will accept answers that demonstrate that this is the case.
EDIT: response to DVK.
TSQL is MSSQLServer's dialect of SQL.
This script works, and so equality can be performed deterministically between float types:
DECLARE #X float
SELECT top 1 #X = RandomGrouping
FROM MyTable
WHERE RandomGrouping BETWEEN 0.839110948199148 - 0.000000000001
AND 0.839110948199148 + 0.000000000001
--yields two records
SELECT *
FROM MyTable
WHERE RandomGrouping = #X
I said "approximately" because that method tests for a range. With that method I could get values that are not equal to my intended value.
The linked article doesn't apply because I'm not (intentionally) trying to straddle the world boundaries between decimal and float. I'm trying to work with only floats. This isn't about the non-convertibility of decimals to floats.
Response to Zinglon:
A literal value that can find my records, thanks.
DECLARE #Y binary(8)
SET #Y = 0x3FEAD9FF34076378
SELECT *
FROM MyTable
WHERE convert(binary(8), RandomGrouping) = #Y
is that literal a floating point literal? Or is it really a decimal
literal that gets converted later?
If my literal is not a floating point literal, what is the syntax for
making a floating point literal?
The 0.867153569942739 literal in SQL Server is a decimal type, not float.
The engine automatically picks appropriate scale and precision to represent the given literal.
To write a literal of the float type you should use the scientific notation, like this:
0.867153569942739E0
This is documented in Constants (Transact-SQL)
decimal constants
decimal constants are represented by a string of
numbers that are not enclosed in quotation marks and contain a decimal
point.
The following are examples of decimal constants:
1894.1204
2.0
float and real constants
float and real constants are represented by
using scientific notation.
The following are examples of float or real values:
101.5E5
0.5E-2
The sp_describe_first_result_set can tell us the types of columns
EXEC sp_describe_first_result_set N'SELECT 0.867153569942739, 0.867153569942739E0'
It returns numeric(15,15) for the first column and float for the second.
If your column RandomGrouping is indexed, it is much more efficient to use a float literal, because when you wrap RandomGrouping in convert(), an index can't be used.
The following query will use an index:
SELECT *
FROM MyTable
WHERE RandomGrouping = 0.867153569942739E0
The following query will not use an index:
SELECT *
FROM MyTable
WHERE convert(binary(8), RandomGrouping) = #Y
It is possible that the values are being truncated on display. I'm assuming the column doesn't have a unique constraint on it, otherwise the question would be moot. On my setup, SSMS truncates the more precise value in this script.
create table flt ( f float not null primary key )
insert into flt
select 0.111111111111111
union all
select 0.1111111111111111
select f, cast(f as binary(8)) from flt
Similarly, if these values are distinct you can cast them to binary(8) and identify them based on that value, like this:
select f from flt
where cast(f as binary(8)) = 0x3FBC71C71C71C71C
The problem is not whether it's a floating point literal or not.
The problem is that comparing two floats for equality in Sybase (or any DB server) is not deterministic, since 4.00000000000000000000... and 3.99999999999999999999... are the same exact number but aren't equal.
Your second solution is the only correct way to compare floats for "equality" (that is, are they the same up to a precision).
Why are you saying "approximately working" about your second approach?
Since you didn't provide the specific DB server you use, here's a fairly decent write-up of the problem (with basically the same conclusions as above) for MySQL
http://dev.mysql.com/doc/refman/5.0/en/problems-with-float.html