The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My work

  • My badges

  • Twitter Updates

  • My Flickr Stream

    20140508-Delphi-2007--Project-Options--Cannot-Edit-Application-Title-HelpFile-Icon-Theming

    20140430-Fiddler-Filter-Actions-Button-Run-Filterset-now

    20140424-Windows-7-free-disk-space

    More Photos
  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 1,723 other followers

A small table that shows differences between decimal, double and float (Single)

Posted by jpluimers on 2013/10/16

Though you programmers all should have read What Every Computer Scientist Should Know About Floating-Point Arithmetic.

But I know you don’t, so below is a small difference on which floating point comparisons fail when using float (f, Single), double (d) and decimal (m, money) values in C#:

0.1 fd_cast_none fd_cast_up fm_cast_none dm_cast_none
0.3 fd_cast_none fd_cast_up fm_cast_none dm_cast_none
0.7 fd_cast_none fd_cast_up fm_cast_none dm_cast_none
0.5 fm_cast_none dm_cast_none
0.9 fd_cast_none fd_cast_up fm_cast_none dm_cast_none

The can help you decide what kind of floating point type you want to use, for instance to answer c# – When should I use double instead of decimal? – Stack Overflow.

I specifically choose the values 0.1, 0.3, 0.5, 0.7 and 0.9 to stress the difference between binary and decimal representations. Apart from the decimal type, you cannot store these decimal values in a binary representation. You can see the decimal representation for a double using the DoubleConverter class (thanks Jon Skeet!)

If you are have a problem that isn’t suite for floating point, then don’t use it. Use rational types, IntXBigInteger or Complex from the System.Numerics namespace, or arbitraty precision floating point numbers.

The failing methods are part of a bigger DecimalDoubleSingleTestProject, for which you will find the source at BeSharp.net repository (you can browse the sources, and access it through SVN and TFS).

That project contains more checks (see the table at the end which includes 0.100000000001 and 0.1222222222222222222221 based on the accuracy you can get with float/double/decimal) and the failing/succeeding methods are the same.

This is what the failing fd_cast_none, fd_cast_up, fm_cast_none and dm_cast_none methods do:

//...
    [TestClass]
    public class UnitTestBase
    {
        protected float f { get; private set; }
        protected double d { get; private set; }
        protected decimal m { get; private set; }
//...
        public void TestMethod_fd_cast_none()
        {
                Assert.AreEqual(f, d);
        }
//...
        public void TestMethod_fd_cast_up()
        {
                Assert.AreEqual((double)f, d);
        }
//...
        public void TestMethod_fm_cast_none()
        {
                Assert.AreEqual(f, m);
        }
//...
        public void TestMethod_dm_cast_none()
        {
                Assert.AreEqual(d, m);
        }
    }

The methods seem to succeed, but that is just the deceiving part: when you carefully select the values you compare, all checks will eventually fail.

The table at the end shows some more literals that fail other tests. It is caused by all these types have different storage formats.

<h3>Conclusion</h3>

When comparing floating point literals, make sure they are of the same type, and select the type according to what precision or representation features you need.

Note this gets even worse when you start calculating with floating points. You will almost always loose accuracy, watch rounding errors and you cannot even do direct AreEqual comparisons any more. Read the articles by Eric Lippert tagged floating+point+arithmetic – Fabulous Adventures In Coding.

–jeroen

Full table:

0.1 fd_cast_none fd_cast_up fm_cast_none dm_cast_none
0.3 fd_cast_none fd_cast_up fm_cast_none dm_cast_none
0.5 fm_cast_none dm_cast_none
0.7 fd_cast_none fd_cast_up fm_cast_none dm_cast_none
0.9 fd_cast_none fd_cast_up fm_cast_none dm_cast_none
0.100000000001 fd_cast_none fd_cast_up fm_cast_none fm_cast_up dm_cast_none
0.300000000003 fd_cast_none fd_cast_up fm_cast_none fm_cast_up dm_cast_none
0.500000000005 fd_cast_none fd_cast_up fm_cast_none fm_cast_up dm_cast_none
0.700000000007 fd_cast_none fd_cast_up fm_cast_none fm_cast_up dm_cast_none
0.900000000009 fd_cast_none fd_cast_up fm_cast_none fm_cast_up dm_cast_none
0.1222222222222222222221 fd_cast_none fd_cast_up fm_cast_none fm_cast_up dm_cast_none dm_cast_up
0.3222222222222222222223 fd_cast_none fd_cast_up fm_cast_none fm_cast_up dm_cast_none dm_cast_up
0.5222222222222222222225 fd_cast_none fd_cast_up fm_cast_none fm_cast_up dm_cast_none dm_cast_up
0.7222222222222222222227 fd_cast_none fd_cast_up fm_cast_none fm_cast_up dm_cast_none dm_cast_down dm_cast_up
0.9222222222222222222229 fd_cast_none fd_cast_up fm_cast_none fm_cast_up dm_cast_none dm_cast_up
144444444444444444444.1 fd_cast_none fd_cast_up fm_cast_none fm_cast_up dm_cast_none dm_cast_down dm_cast_up
344444444444444444444.3 fd_cast_none fd_cast_up fm_cast_none fm_cast_up dm_cast_none dm_cast_up
544444444444444444444.5 fd_cast_none fd_cast_up fm_cast_none fm_cast_up dm_cast_none dm_cast_up
744444444444444444444.7 fd_cast_none fd_cast_up fm_cast_none fm_cast_up dm_cast_none dm_cast_up
944444444444444444444.9 fd_cast_none fd_cast_up fm_cast_none fm_cast_up dm_cast_none dm_cast_up

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: