Solved

Format float as varchar

Posted on 2006-06-22
8
6,275 Views
Last Modified: 2010-08-05
Hi, I have searched EE, but I must not be searching the correct keywords, as I can't find an answer to this one.

I have a column of type float "my_ref"
and I am just trying to append the value of this column to the value in the "name" column - which is a varchar

select my_ref, my_id, ( +'('+  cast(my_ref as varchar(255)) + ') '+ name) as my_ref_name from my_table




Sample data:

my_ref                                       name
7.0010000000000003                  The name



So when I run the query, I would expect  the following (or I want this to happen):


(7.0010000000000003) The name




But instead, I get this:

(7.001) The name





Is there a way that I can get these results without changing the datatype from float?
If not, what bad things will happen if I change the datatype?

thanks!
0
Comment
Question by:cdfllc
  • 3
  • 3
  • 2
8 Comments
 
LVL 4

Assisted Solution

by:kenpem
kenpem earned 250 total points
ID: 16964395
try this:

select my_ref, my_id, ( +'('+  ltrim(str(my_ref,30,20)) + ') '+ name) as my_ref_name from my_table

you may have to play with the number of decimals!
0
 
LVL 32

Expert Comment

by:bhess1
ID: 16964488
You have run into one of the many limitations of using float and real numbers, and one reason why I always use numeric/decimal instead.  Consider this test script:

SET NOCOUNT ON
CREATE TABLE #Test1 (f1 float)

INSERT INTO #Test1 VALUES (15.0003)
INSERT INTO #Test1 VALUES (15.00003)
INSERT INTO #Test1 VALUES (15.000003)
INSERT INTO #Test1 VALUES (15.0000003)
INSERT INTO #Test1 VALUES (15.00000003)
INSERT INTO #Test1 VALUES (15.000000003)

SELECT f1, Cast(f1 AS varchar(16)) AS CastF1,
                  convert(varchar(16), f1) AS ConvF1,
                  convert(varchar(16), f1, 1) AS [ConvF1-1]
FROM #Test1

DROP TABLE #Test1


When run, the output of the script is:

f1                                                    CastF1           ConvF1           ConvF1-1        
----------------------------------------------------- ---------------- ---------------- ----------------
15.000299999999999                                    15.0003          15.0003          1.5000300e+001
15.000030000000001                                    15               15               1.5000030e+001
15.000003                                             15               15               1.5000003e+001
15.0000003                                            15               15               1.5000000e+001
15.000000030000001                                    15               15               1.5000000e+001
15.000000003                                          15               15               1.5000000e+001

Here, you see two sources of imprecision:  rounding errors and Cast/Convert limitations.  I assume that you already know about rounding issues, so let's talk about Cast/Convert limitations.

As you see, only the 15.0003 value has all of its digits shown in Cast, or in Convert with no Format specified.  In the specified format 1 in convert, you can see the value out to 8 total digits, but no more.  This is actually covered (obscurely) in BoL, which states (in part):

This table shows the style values for float or real conversion to character data.

Value Output
0 (default) Six digits maximum. Use in scientific notation, when appropriate.
1 Always eight digits. Always use in scientific notation.
2 Always 16 digits. Always use in scientific notation.

So, by default, CAST and CONVERT will only show six digits of the FLOAT value, no matter what precision it has.  CAST will never show anything else, but CONVERT can be made to show up to 16 digits, but only in scientific notation.

This is not a bug, but (IMO) a bad design decision.
0
 
LVL 4

Expert Comment

by:kenpem
ID: 16964489
If this is something you do often, you could even

CREATE FUNCTION fFloatToStr(@This FLOAT) RETURNS VARCHAR(30) AS
BEGIN
  DECLARE @Res VARCHAR(30)
  SET @Res=LTRIM(STR(@This,30,20))
  WHILE RIGHT(@Res,1) = '0'
    SET @RES=LEFT(@Res,LEN(@Res)-1)
  RETURN @Res
END

and then, for example

select my_ref, my_id, ( +'('+  dbo.fFloatToStr(my_ref)  + ') '+ name) as my_ref_name from my_table

which is a little more readable.

Watch out for floats! They can be horribly inaccurate at the kind of precision you are working at there. Your example seems to be OK, but simply converting 7.001000000005 to a string yields a result of 7.0010000000049999 !
0
 
LVL 32

Expert Comment

by:bhess1
ID: 16964538
One additional comment.  The value 7.001 is actually what was inserted into the column.  Try it and see -- a value of 7.001 inserted returned 7.0010000000000003 from a float field for me.
0
PRTG Network Monitor: Intuitive Network Monitoring

Network Monitoring is essential to ensure that computer systems and network devices are running. Use PRTG to monitor LANs, servers, websites, applications and devices, bandwidth, virtual environments, remote systems, IoT, and many more. PRTG is easy to set up & use.

 
LVL 4

Expert Comment

by:kenpem
ID: 16964558
oh the joys of FLOAT!
0
 
LVL 1

Author Comment

by:cdfllc
ID: 16969312
Yes, in the defense of the person that built this application originally, I don think this field is being used for it's original purpose.
But I will know for any future enhancements to stay away from float.

So can any of you guys help me convert to decimal?
I just went and changed the datatype of the field, from float to decimal (not on the production table, of course) but now it has removed all the numbers after the decimal place...
So what was originally 7.001 is now 7
0
 
LVL 32

Accepted Solution

by:
bhess1 earned 250 total points
ID: 16970733
How did you define the decimal field/ conversion?  

I would probably look over the values in the field, and see what the values were when converted to Decimal(19,6) (this means up to 19 digits, with 6 allowed after the decimal point) or Decimal (19,8).  If the largest actual number of positions after the decimal point was only 3 (as in your example), I would probably define the field (or Convert statement) as Decimal(9,3).  This would allow for 6 digits before the decimal point, and 3 after, e.g.:

SELECT Cast(my_ref as Decimal(9,3))
0
 
LVL 1

Author Comment

by:cdfllc
ID: 17001624
Thanks guys/gals - I used a combination of your answers:

select ( +'('+  convert(nvarchar, Cast(my_ref as Decimal(9,3) )) + ') '+ name) name  from mytable

This gives results such as this:

(700.159) The name
0

Featured Post

Optimizing Cloud Backup for Low Bandwidth

With cloud storage prices going down a growing number of SMBs start to use it for backup storage. Unfortunately, business data volume rarely fits the average Internet speed. This article provides an overview of main Internet speed challenges and reveals backup best practices.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Having an SQL database can be a big investment for a small company. Hardware, setup and of course, the price of software all add up to a big bill that some companies may not be able to absorb.  Luckily, there is a free version SQL Express, but does …
The Delta outage: 650 cancelled flights, more than 1200 delayed flights, thousands of frustrated customers, tens of millions of dollars in damages – plus untold reputational damage to one of the world’s most trusted airlines. All due to a catastroph…
Using examples as well as descriptions, and references to Books Online, show the documentation available for date manipulation functions and by using a select few of these functions, show how date based data can be manipulated with these functions.
Viewers will learn how to use the SELECT statement in SQL and will be exposed to the many uses the SELECT statement has.

910 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

22 Experts available now in Live!

Get 1:1 Help Now