I agree with many of the earlier comments. The only good thing about this question is the debate it provokes - plus it may discourage people from careless use of STR instead of cast and convert. The nonsense explanation contains a reference to a BoL page whose only relevance is that it points out that the first argument of STR is typed FLOAT (ie FLOAT(53), as 53 is the default precision for FLOAT), and does not in the least support the explanation provided.
As Hugo rightly says, the bizarre rounding effect is caused by the representation error. The representation error in SQL Server's float type is different in kind from the representation error in its so called exact numerics for one simple reasons: the floating point uses an outdated version of the IEEE 754 floating point standard. For now we are stuck with this, and because we know that the argument of DTR, if not already float, is implicitly converted to float before STR gets its hand on it, an alternative to STR is needed for use with exact numeric types. This is actually very easy to build from cast and convert:
-- instead of
-- which delivers incorrect rounding caused by representation errors introduced by
-- the implicit conversion to float.
-- For exact numerics we can use
convert(varchar(length),cast( as numeric(length-1,decimal)))
-- provided decimal > 0.
-- When decimal is 0, we need
convert(varchar(length),cast( as numeric(length,0)))
-- instead, because the decimal point doesn't take up a space.
-- if, like me, you think that usually when a floating point number's decimal
-- representation ends up in something like 49999 it is a representation error
-- for 50000, you can use
convert(varchar(length),cast(cast(x as numeric(2+length,1+decimal))
-- which first rounds to one place more than required, so that representation
-- inaccuracy that might affect the result is eliminated.
-- as before, use length instead of length-1 if the required number
-- of decimal places is 0.
-- But don't use this on floats or reals if you think they are likely to have accurate
-- values that really do end in 49999999 or similar (with the 49999 beyond the last
-- position in the required output).
There are two problems with the outdated floating point standard: (a) it provides a maximum size of 64 bits for a floating point quantity, hence a maximum precision of 53 and (b) it insists on an exponent base inapproporiate for use when data is entered using decimal notation.
When (or do I mean if?) the 2008 revision of the floating point standard is available in SQL Server we will be able to use 128 bit floats with a decimal base for the exponent, so there will be no representation error converting from decimal notation, only precision error, and the precision error will of course be less than with 64 bit floats (the precision will still be less than 38 decimal digits, but at 36 decimal digits it's not much less).
The rounding mentioned in the Using decimal, float, and real DataBol page has nothing to do with this - that's about binary rounding in the course of arithmetic, which impacts coversion to float not from it. What we have here is decimal rounding done by STR - which is to the closest value, while SQL Servers implementation of the binary rounding in floating point is always up, as stated on that BoL page. Of course the value handed to STR isn't 3.65, it's 3.64999999999999 accurate to 15 decimal digits (maybe even 3.6499999999999 accurate to 16) so it is actually less than 3.65, which is why STR delivers 3.6 instead of 3.7.