user1269009
user1269009

Reputation: 527

.NET AddDays issue

The next 2 lines adds the same amount to the same date, and the results date part is the same, but somehow the there's difference in the time part!

(new DateTime(2000,1,3,18,0,0)).AddDays(4535);   
(new DateTime(2000,1,3,18,0,0)).AddMonths(149);

you'll get a difference of 15 secs, and with both are at least roundable to days, I don't know why this happend, but it happens only with AddDays, but not AddMonths (even with thousands of months added)


Edit 1

So I've tried to make a sample project, but no luck. If I run my main project, and put the sample lines into the watches, than I get 2 separate values, if I make a fresh start, the problem is not there. The project is 3.5, c#, vs2010, win7hp x64 (proj: x86). I'm trying to reproduce it also in a fresh small project, I'll be writing back if I have it.

These are my results in the main project (copeid from watches!):

(new DateTime(2000, 1, 3, 18, 0, 0)).AddDays(4535).Ticks    
 634743432153600000 long

(new DateTime(2000, 1, 3, 18, 0, 0)).AddMonths(149).Ticks   
 634743432000000000 long

Edit 2

I've managed to narrow it down even more. We have a self-made component, panel base, we draw on it with directx. If I make that visible=false, than visible=true, than the error comes, before the visible=true (or show()), the calculation is correct. What in the world can be there, that the result gets something else of a formula where no variable is used. Culture is not affected in the component..

Upvotes: 12

Views: 611

Answers (2)

Jeffrey Sax
Jeffrey Sax

Reputation: 10313

This is a result of DirectX silently changing the floating-point calculation mode of the CPU to always use single precision. This is sometimes done for performance: using single precision can be quite a bit faster than using double precision. See the description of the FpuPreserve flag in the MSDN documentation for the DirectX CreateFlags enumeration.

The reason others aren't able to reproduce this is because they are not doing those DirectX calls.

The argument to AddDays is a double. This value is multiplied by a scale factor to get the time in milliseconds. It is this calculation that produces the error.

Consider:

double value = 4535;
int scale = 86400000;
long milliseconds = (long) ((value * scale) + ((value >= 0.0) ? 0.5 : -0.5));
long milliseconds2 = (long)((float)(value * scale) + ((value >= 0.0) ? 0.5 : -0.5));
Console.WriteLine(milliseconds2 - milliseconds);

The expression for milliseconds2 contains a cast to float, which mimics the effect of DirectX forcing single-precision calculations. This will print 15360, exactly the difference you find.

By contrast, AddMonths takes an integer, and does not use any floating-point arithmetic. So the result is exact.

Upvotes: 3

xanatos
xanatos

Reputation: 111810

Here they give the same result:

var d1 = (new DateTime(2000, 1, 3, 18, 0, 0)).AddDays(4535).Ticks;
var d2 = (new DateTime(2000, 1, 3, 18, 0, 0)).AddMonths(149).Ticks;

d1 == d2 == 634743432000000000

(the Tick is the internal "quantum" of time of DateTime. It's quite short. It's one ten-millionth of a second

I'll add that even Mono (an independent implementation of .NET) gives the same result http://ideone.com/krySY (Ideone uses mono)

Considering the more recent things you wrote, it's quite easy: memory corruption. Memory corruption can do very random things. This probably is one of those :-)

Upvotes: 4

Related Questions