Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com

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?

Posted 11-05-2008 2:13 PM by Tim Barcz

[Advertisement]

on 11-05-2008 4:00 PM

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.

on 11-05-2008 4:31 PM

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.

on 11-05-2008 5:16 PM

Simple question then:

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

on 11-05-2008 5:50 PM

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.

on 11-05-2008 6:57 PM

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.

on 11-05-2008 7:12 PM

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.

on 11-05-2008 7:19 PM

> 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?

on 11-05-2008 7:26 PM

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.

on 11-07-2008 3:40 AM

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.

on 11-10-2008 1:08 PM

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

on 04-05-2011 8:53 PM

Exercise caution when using floating point numbers.. Nice :)

on 04-22-2011 9:40 AM

Exercise caution when using floating point numbers.. OMG! :)

on 05-01-2011 3:18 AM

Exercise caution when using floating point numbers.. Ho-o-o-o-t :)

on 06-02-2011 8:31 PM

Exercise caution when using floating point numbers.. Dandy :)

on 06-03-2011 1:28 AM

Exercise caution when using floating point numbers.. He-he-he :)

on 06-04-2011 8:51 PM

Exercise caution when using floating point numbers.. Retweeted it :)

on 06-05-2011 1:46 AM

Exercise caution when using floating point numbers.. Nice :)

on 06-14-2011 6:10 AM

Exercise caution when using floating point numbers.. Smashing :)

on 07-02-2011 2:35 AM

Exercise caution when using floating point numbers.. Ho-o-o-o-t :)

on 07-02-2011 7:33 AM

Exercise caution when using floating point numbers.. Reposted it :)

on 07-02-2011 7:31 PM

Exercise caution when using floating point numbers.. Slap-up :)

on 01-18-2013 11:43 PM

dQ7urN Muchos Gracias for your blog. Want more.

on 02-01-2013 2:26 PM

22ZMkA Thanks a lot for the blog article.Really thank you! Much obliged.

on 02-02-2013 8:15 PM

Kvhsxq I value the blog post.Really thank you! Cool.

on 03-01-2013 1:05 AM

hyY7nr I value the blog.Really looking forward to read more. Keep writing.

on 03-14-2013 8:46 AM

vIy1RY Thanks so much for the article post.Really thank you! Great.

on 03-22-2013 10:17 PM

qh9165 Thanks for sharing, this is a fantastic post.Really looking forward to read more. Keep writing.

on 04-03-2013 5:07 AM

FxfEYg I appreciate you sharing this blog post.Much thanks again. Fantastic.

on 06-21-2013 12:40 AM

n0wNxn Great, thanks for sharing this blog.Thanks Again. Really Great.

on 08-03-2013 9:39 PM

GyFv5l Thanks again for the blog article.Thanks Again. Much obliged.

on 09-12-2013 7:33 PM

I64kLF Very informative post.Really thank you! Awesome.

on 10-16-2013 1:33 AM

nLc8Ot Thank you for your article.Much thanks again. Really Great.

on 10-23-2013 6:37 PM

1DQVp0 Really enjoyed this post. Much obliged.

on 10-31-2013 9:56 PM

vpgwpg Thanks-a-mundo for the article.Really looking forward to read more. Really Great.