Devlico.Us
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @devlicious

Tim Barcz

Why use nails when a screw is the more reversible choice? Have Twitter, follow the conversation there at @timbarcz


Exercise Caution When Using Floating Point Numbers

I understand floating point numbers can be somewhat imprecise but I'm a bit bothered today when I see the following evaluates to false:

   1: float f = .16f;
   2: double d = .16d;
   3:  
   4: Assert.That(f, Is.EqualTo(d))

Annoyingly I wanted to see what the two values are:

   1: Console.WriteLine((double)f)
   2: Console.WriteLine(d);

produces the following respectively:

.159999996423721

.16

What bothers me about this, and I'm hoping someone can eloquently explain this, the MSDN docs say this should be implicitly converted.  From the MSDN doc article on implicit numeric conversions:

  • From float to double.

Float is 32-bit and double is 64-bit, so why can't the float fit nicely inside the address space for the double?

Hell, if you're not going to respect the integrity of the number what's the point of the implicit conversion?


Published Nov 05 2008, 02:13 PM by Tim Barcz
Filed under: ,

Comments

Kelly Leahy said:

Tim, the problem is that 0.16 is not the "real" value of the float either.  The real value of the float is, in fact, 0.159999996423721, since IEEE floating point numbers can't exactly store 0.16 (ever).  At least, the value I get from Excel is: 0.159999996423721.

The floating point string formatting routines in .NET and other languages are "smart" and actually show this as 0.16, but in reality that is the value that's in the 32-bit IEEE number.

That said, when you convert to double, you get 29 more 'digits' of accuracy (29 more bits, actually), and so these all get filled with zeros (since there's no way for .NET to know that you meant 0.16 when you "said" 0.159999996423721.

Hopefully this helps.  If you want more details you can check Wikipedia's Single_precision and Double_precision pages.

I also have a spreadsheet that does these calculations in Excel if you're interested in seeing the details.  I can post it on my blog if there's interest.

# November 5, 2008 4:00 PM

Igor Ostrovsky said:

Kelly is right. The precision is not getting lost in the conversion. The problem is that 0.159999996423721 is the best possible float representation of 0.16.

# November 5, 2008 4:31 PM

Tim Barcz said:

Simple question then:

Why isn't double d = .16; returning the same value?

# November 5, 2008 5:16 PM

Jean-Francois Cantin said:

Tim, I went through this a while back and was disappointed to find that Kelly is right. If you absolutely require 0.16, the only way to get that, is to use decimal d = 0.16.

# November 5, 2008 5:50 PM

Kevin H said:

If you translate this to base 10, and assume that a float is 3 significant figures and double is 6, then the following would be similar:

float f = 1/32f;         // f = 1.56 * 10^-2

double d = 1/32d;  // d = 1.56250 * 10^-2

Assert.That(f, Is.Equal(d));

Sure, the float gets converted to a double, but that means the expanded float is 1.56000, which clearly isn't 1.56250, and the assertion fails.

It's always best to compare the absolute difference being smaller than a threshold than equality when talking about floats.. Even something simple like:

float f = 0;

for (int i=0; i<100; i++)

 f += 0.1f;

Assert.That(f, Is.Equal(10.0f));

would fail, but for different reasons.

# November 5, 2008 6:57 PM

Kevin H said:

Reflecting my comments in binary,

.16 (decimal) = .0010100011110101110000101001 (binary), exactly

This gets stored in a floating point as:

[1.]01000111101011100001010 x 2^-3

And stored in double as:

[1.]0100011110101110000101001 x 2^-3

When the float gets promoted to double, it can't know about the extra digits.

# November 5, 2008 7:12 PM

Kelly Leahy said:

> why isn't d = 0.16 (double) returning the same value

because double d = 0.16D also ISN'T 0.16.  Think about it this way.  If I asked you to store 1/7 in decimal in 20 digits, then write it to disk, then read it from disk, and convert it to 40 digits, would it match the result if I told you to store 1/7 in 40 digits in the first place?  The point is, by the time you 'store' 1/7 in a specified number of decimal digits, you have truncated it to something other than 1/7.  Then, when you convert it to some other number of digits, you've already lost the 'real' value so there's no way to know exactly how you're supposed to convert it.

Does this make sense?

# November 5, 2008 7:19 PM

Kelly Leahy said:

BTW, I forgt to mention (clearly) that

double d = 0.16

is also not 0.16.  It's 0.16 - 2.22E-16 approximately.

this means that if you were to do something like come up with a 'real' way to compute 0.16 using numbers that CAN be precisely represented in double precision numbers, and you did this math inside the floating point support for the CPU (which uses 10 byte - 80bit arithmetic internally), and then loaded your 0.16 from the 8-byte (64bit) number and subtracted it, you'd get a number that isn't zero...

In other words, if you could do something like:

x = foo(a,b,c)   where a,b,c are double (IEEE 64-bit), and x is the IEEE 80-bit FPU result, then do

y = 0.16D

z = x - y

z will not be zero, due to the fact that y = 0.16D zero extends 0.16, while x is an 80-bit representation of 0.16.

# November 5, 2008 7:26 PM

Ian Cooper said:

Comparing floating point numbers is one of those 'looks harder than it is' issues. The usual technique is to check that the delta between them is not greater than a certain fraction. There is a pretty good summary here:

www.cygnus-software.com/.../comparingfloats.htm

This one suprises a lot of people at first.

# November 7, 2008 3:40 AM

Code Monkey Labs said:

Pick of the week: Advice For Developers On Starting An Independent Software Vendor (ISV) Business General Tips For Preparing For A Technical Presentation : Scott Hanselman shares how he prepares for technical presentations. LINQ Cheat Sheet : Brad Vincent

# November 10, 2008 1:08 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add
Check out Devlicio.us!

Our Sponsors

Proudly Partnered With