2

I have the following code:

DateTime PunchIn = DateTime.Parse("12/10/2015 5:06:00 PM");
DateTime PunchOut = DateTime.Parse("12/10/2015 5:30:00 PM");
double WorkedTime;
WorkedTime = (PunchOut - PunchIn).TotalHours;

MessageBox.Show(WorkedTime.ToString());

The value of WorkedTime should be 0.4 (24/60).

But as you can see from the following screenshot, the calculated value is 0.39999999...

screenshot1

But to make it even more confusing, the MessageBox displays the calculated value as 0.4

screenshot2

Can anyone help explain this seemingly bizarre behavior?

EDIT: If this is a "floating point inaccuracy", what is the work around so I can get the answer of 0.4 in my code?

Community
  • 1
  • 1
Joe Gayetty
  • 1,521
  • 2
  • 22
  • 35
  • 1
    Seems like a floating point precision bug. http://stackoverflow.com/questions/2100490/floating-point-inaccuracy-examples – James Gaunt Dec 03 '15 at 18:32
  • 5
    @JamesGaunt not really a bug just the nature of floating point numbers – Marco Fatica Dec 03 '15 at 18:33
  • 4
    @MarcoFatica yes, fair point, we'll call it a "feature" then :) – James Gaunt Dec 03 '15 at 18:33
  • `ToString()` delivers the default format in the given context. When you deal with variables where the string format matters (e.g., a number or a date), this behaviour might not be the one you want. Bottom line: never use `ToString()` without any argument when the format matters unless being completely sure that you know the format in the given context. In this case, you should input as argument the number of decimal positions to be displayed. – varocarbas Dec 03 '15 at 18:34
  • Change WorkedTime to decimal instead of double – user469104 Dec 03 '15 at 18:35
  • @varocarbas, the first part of the question (why is the value 3.999999 and not the 'correct' answer of 4) is due to the nature of floating point numbers. The second part is due to string formatting. – James Gaunt Dec 03 '15 at 18:36
  • @JamesGaunt OK. Good correction. I was focused on the second part :) – varocarbas Dec 03 '15 at 18:37
  • Anyone have a work around for the floating point precision bug? – Joe Gayetty Dec 03 '15 at 18:48
  • american date format is stupid. just putting that out there. might as well be month/day/year second:hour:minute. but yeah. – Dbl Dec 03 '15 at 18:50
  • @jgayetty--it's not really a bug, but simply the nature of using floating points. Use decimal instead of double--decimals have significantly higher precision, and this might be sufficient to address the issue. – Russ Dec 03 '15 at 18:51

1 Answers1

5

Many decimal numbers cannot be represented as binary floating point numbers without precision loss. Just like 1/3 cannot be represented as a finite decimal number.

What you get, is the closest number that can be represented in the chosen format.

You have 2 options to get 0.4 as a result in your code:

You can use Math.Round to cut the insignificant digits:

workedTime = Math.Round(timeSpan.TotalHours, 3)

Or you can use decimal and divide TotalMinutes by 60 yourself:

decimal workedTime = (decimal)timeSpan.TotalMinutes / 60;

ToString() with no parameters or "G" format specifier will automatically round the double value to 15 digits.

0.3999999999999997.ToString() // "0.4"
0.399999999999997.ToString()  // "0.399999999999997"
Jakub Lortz
  • 14,616
  • 3
  • 25
  • 39
  • I chose to use Math.Round to 4 digits since I know my TimeSpans will never need more than 4 digits (I know this because I explicitly control the input). I admit though, I cannot understand why the developers of the framework (and other languages) cannot make 24/60 = .4 It reminds me of the stories you hear that someone created some flawed methodology 40 years ago and everyone since has followed it...flaws and all. – Joe Gayetty Dec 03 '15 at 19:26