And that’s why… you don’t use floating point for currency values

by acha11 9. June 2009 19:17

Say it with me:

Floating point is not precise.

Everybody knows it, but it’s worth re-inking the tattoo every few weeks.

Here’s what happens when you maintain a running total using single-precision floating point, double-precision floating point, and .NET’s Decimal (precise) types:

                                           Running totals

        Add                 Single              Double             Decimal
+     0.00 =                    0                   0                   0
+   265.14 =               265.14              265.14              265.14
+   556.21 =           821.350037              821.35              821.35
+   989.34 =           1810.69006             1810.69             1810.69
+   611.05 =              2421.74             2421.74             2421.74
+   880.13 =           3301.86987             3301.87             3301.87
+   252.96 =           3554.82983             3554.83             3554.83
+   953.09 =              4507.92             4507.92             4507.92
+   704.50 =              5212.42             5212.42             5212.42
+   773.02 =              5985.44  5985.4400000000005             5985.44
+   141.35 =              6126.79  6126.7900000000009             6126.79
+   861.57 =              6988.36  6988.3600000000006             6988.36
+   555.05 =           7543.40967  7543.4100000000008             7543.41
+   331.99 =               7875.4  7875.4000000000005             7875.40
+    68.30 =           7943.69971  7943.7000000000007             7943.70
+   126.06 =              8069.76  8069.7600000000011             8069.76
+    93.85 =              8163.61  8163.6100000000015             8163.61
+   371.99 =               8535.6  8535.6000000000022             8535.60
+   640.86 =              9176.46  9176.4600000000028             9176.46
+   791.58 =              9968.04  9968.0400000000027             9968.04
+    48.06 =              10016.1  10016.100000000002            10016.10
+   994.79 =             11010.89  11010.890000000003            11010.89
+   462.06 =           11472.9492  11472.950000000003            11472.95
+   894.17 =           12367.1191  12367.120000000003            12367.12
+    73.58 =           12440.6992  12440.700000000003            12440.70
+   928.79 =           13369.4893  13369.490000000002            13369.49
+   912.05 =           14281.5391            14281.54            14281.54
+   396.61 =           14678.1494  14678.150000000002            14678.15
+   173.86 =             14852.01  14852.010000000002            14852.01
+   530.15 =             15382.16  15382.160000000002            15382.16
+   315.51 =             15697.67  15697.670000000002            15697.67
+    78.32 =             15775.99  15775.990000000002            15775.99
+    50.18 =             15826.17  15826.170000000002            15826.17
+   774.53 =              16600.7             16600.7            16600.70
+   514.21 =             17114.91            17114.91            17114.91
+   509.96 =           17624.8711            17624.87            17624.87
+   888.53 =              18513.4  18513.399999999998            18513.40
+   969.67 =             19483.07  19483.069999999996            19483.07
+   953.47 =            20436.541  20436.539999999997            20436.54
+   182.70 =             20619.24  20619.239999999998            20619.24
+   137.63 =           20756.8711            20756.87            20756.87
+   569.61 =             21326.48            21326.48            21326.48
+   404.25 =             21730.73            21730.73            21730.73
+   443.83 =             22174.56            22174.56            22174.56
+   765.74 =              22940.3  22940.300000000003            22940.30
+   185.64 =           23125.9414  23125.940000000002            23125.94
+    35.75 =           23161.6914  23161.690000000002            23161.69
+   533.13 =           23694.8223  23694.820000000003            23694.82
+   671.39 =           24366.2129  24366.210000000003            24366.21
+   677.42 =           25043.6328            25043.63            25043.63

A little imprecision isn’t the end of the world, obviously, and there are workarounds (rounding to two decimal places). But if you’re cross-checking a precise financial system’s running total against your own imprecise floating-point total, well, you’re going to get false positives every now and then. Choose a precise data type, though, and you won’t have this problem.

Round-trip format is cool

The .NET BCL does some work to protect you against the vagaries of imprecise floating point formats.

image

In the window above, the first row shows that 1.111f can be represented precisely by a float.

The second row shows that 1f + .111f doesn’t equal 1.111f – the precision of float is screwing us over just a little (0.00000006, to be precise).

The third row shows that ToString() does a little rounding to hide the inaccuracy (that was caused by imprecision) in the least-significant digit.

The fourth row shows that ToString(“R”) gives a string that is as faithful a representation .NET can give of the actual float value.

Tags:

Comments

Comments are closed

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

RecentComments

Comment RSS