Does casting double to float always return same value?
Solution 1
The results should not be language dependent, unless the language deviates from the IEEE specification.
All floats can be exactly represented as doubles, so the round trip from float to double to float should yield the same value that you started with.
Similarly, casting any double value to float should always yield the same result, but, of course, there are many different double values that would truncate to the same float value.
Solution 2
If you downcast a double
to a float
, you are losing precision and data. Upcasting a float
to a double
is a widening conversion; no data is lost if it is then round-tripped...that is, unless you do something to the value prior to downcasting it back to a float.
Floating-point numbers sacrifice precision and accuracy for range. Single-precision floats give you 32-bits of precision; double-precision give you 64-bits. But they can represent values way outside the bounds that the underlying precision would indicate.
C# float
and double
are IEEE 754 floating point values.
-
float
is a single-precision IEEE 754 value (32 bits) and consists of a- 1-bit sign
- 8-bit exponent
- 23-bit mantissa/significand
-
double
is double-precision IEEE 754 value (64 bits) and consists of a- 1-bit sign
- 11-bit exponent
- 52-bit mantissa/significand
The effective precision of the mantissa is 1-bit more than its apparent size (floating point magick).
Some CLR floating point resources for you:
- http://csharpindepth.com/Articles/General/FloatingPoint.aspx
- http://www.extremeoptimization.com/resources/Articles/FPDotNetConceptsAndFormats.aspx
This paper is probably the canonical paper on the perils and pitfalls of floating point arithmetic. If you're not a member of the ACM, click the link on the title to find public downloads of the article:
- David Goldberg. 1991. What every computer scientist should know about floating-point arithmetic. ACM Comput. Surv. 23, 1 (March 1991), 5-48. DOI=10.1145/103162.103163 http://doi.acm.org/10.1145/103162.103163
Abstract
Floating-point arithmetic is considered as esoteric subject by many people. This is rather surprising, because floating-point is ubiquitous in computer systems: Almost every language has a floating-point datatype; computers from PCs to supercomputers have floating-point accelerators; most compilers will be called upon to compile floating-point algorithms from time to time; and virtually every operating system must respond to floating-point exceptions such as overflow. This paper presents a tutorial on the aspects of floating-point that have a direct impact on designers of computer systems. It begins with background on floating-point representation and rounding error, continues with a discussion of the IEEE floating point standard, and concludes with examples of how computer system builders can better support floating point.
Solution 3
Considering that they have different precision, even i you're casting from less precision to wider one (I suppose that is actually your doubt) the result can not be always the same.
Floating point operations, especially casting, are always a subject of truncating/rounding and any other type of approximation.
Solution 4
In some cases, the closest float
representation to a numeric quantity may differ from the value obtained by rounding the closest double
representation to a float
. Two such quantities are 12,344,321.4999999991 and 12,345,678.50000000093. The integers above and below both those quantities are precisely representable as float
, but the nearest double
to each of them has a fractional part of precisely 0.5. Because converting such double
values (between 2^23 and 2^24, with a fraction of precisely 0.5) to float
will round to the nearest even integer; the compiler will in each case end up rounding away from the value which would have been closer to the original number.
Note that in practice, the compiler seems to parse numbers as double
, and then convert to float
, so even though 12344321.4999999991f should round to 12344321f, it instead rounds to 12344322f. Likewise 12345678.50000000093f should rounds to 12345679f but rounds to 12345678f, so even in cases where conversion to double
and then float
loses precision, such conversion loss cannot be avoided by specifying numbers directly as float
.
Incidentally, the values 12344321.4999999992f and 12345678.50000000094f are rounded correctly.
Related videos on Youtube
S.L. Barth
Updated on March 01, 2020Comments
-
S.L. Barth about 4 years
Does casting
double
tofloat
always produce same result, or can there be some "rounding differences"?For example, is
x
infloat x = (float)0.123456789d;
always the same value?
What about when casting float to double, and then casting it back to float ie.
(float)(double)someFloat
?Mostly interested in what the results are in C#, but feel free to share if you have knowledge about how this works on other languages.
-
phoog about 12 years@Moozhe Not in C#, where the decimal suffix is "m".
-
Mr Lister about 12 years@Moozhe Wrong,
d
is for double.M
is for decimal (and it's short for "money"). msdn.microsoft.com/en-us/library/bfft1t3c.aspx
-
-
phoog about 12 yearsThis isn't quite true. Binary floating point operations result in approximations when you try to use them to represent decimal numbers. The approximation comes about because of the conversion from one base to another. Float and double are both base-2 data types, so the larger data type can exactly represent any value that the smaller type can.
-
Tigran about 12 years@phoog: don't really understand your point:
(double)4.123401f
is equal in standartToString()
==4.12340116500854
. It's not the same number. Use or not use, the number which is in the cell of double is not the same. -
Tigran about 12 years@phoog: wrong. The binary rapresentation it not the same too.
-
phoog about 12 yearsI take that back. I had been testing the binary values in F# interactive, which is not particularly relevant to a C# discussion. I wrote a C# console program that casts 4.123401f to double and back, and writes the binary representation. The float value is
01000000100000111111001011100111
and the double value is0100000000010000011111100101110011100000000000000000000000000000
; these values are exactly equal. The difference in the ToString output is a result of the fact thatfloat.ToString()
rounds its output to fewer decimal places thandouble.ToString()
-
phoog about 12 yearsThe binary representation of
0.123456789d
is actually0011111110111111100110101101110100110111001110010110001101011111
; the mantissa is(1).1111100110101101110100110111001110010110001101011111
. The corresponding float value is00111101111111001101011011101010
(mantissa(1).11111001101011011101010
). Neither value fits comfortably into the size of a double, let alone a float, beccause123456789/1000000000
repeats infinitely in base 2.