2

There is quite old code that I got, where exist such days delay calculation:

#define _SECOND ((ULONGLONG) 10000000)
#define _MINUTE (60 * _SECOND)
#define _HOUR   (60 * _MINUTE)
#define _DAY    (24 * _HOUR)

FILETIME CurTime;
GetSystemTimeAsFileTime(&CurTime);

ULONGLONG qwCurResult = (((ULONGLONG)CurTime.dwHighDateTime) << 32) + CurTime.dwLowDateTime;

DWORD days = (qwCurResult - SomeULONGLONGMoment) / _DAY;

and of course I receive message

warning C4244: 'argument' : conversion from 'ULONGLONG' to 'DWORD', possible loss of data

At modern VS2013 compiler. I know, that it will be number of full days between two moments that is possible to store in DWORD. How to avoid this message?

I don't want to disable all warnings with this number, because somewhere else they may be very useful. Does there exist right way to avoid possible loss of data? I can't counting days in any type except DWORD (or I will just move place with this warning to other part of code).

If you think it is not avoidable, and best solution will be to use another mechanism of getting current date - I will be able to use it just if there will be way to convert SomeULONGLONGMoment (that is ULONGLONG) to types it uses.

Arkady
  • 2,084
  • 3
  • 27
  • 48
  • 1
    `static_cast` the whole expression? – Some programmer dude Jun 28 '14 at 14:51
  • 1
    There is a loss of data, your program is not Y1M ready. Not exactly much to worry about right now, you use a cast to tell the compiler that you don't care. – Hans Passant Jun 28 '14 at 14:55
  • you mean static_cast((qwCurResult - SomeULONGLONGMoment) / _DAY) ? As I understand, static_cast allows compiler to check types at compile time, will it allow such conversation? – Arkady Jun 28 '14 at 14:58
  • @HansPassant, what means Y1M? :-) Am I right that good ways is to you dynamic cast: it will throw me exception at runtime if there will be real loss of data, and work silently while everything is OK? – Arkady Jun 28 '14 at 15:05
  • It is Y2K times 500 something. No, no magic exceptions unless you use a safeint library, use a static cast or C cast. – Hans Passant Jun 28 '14 at 15:21
  • +1 for trying to fix the problem rather than just disabling warnings. :-) – Harry Johnston Jun 29 '14 at 01:27

1 Answers1

5

The problem is that an ULONGLONG type is a 64 bit unsigned number while the DWORD type only can hold 32 bits of an unsigned number. If you know for certain that DWORD can hold the calculated number, you could probably simply cast it to a DWORD by saying

DWORD days = static_cast<DWORD>((qwCurResult - SomeULONGLONGMoment) / _DAY);

However as stated, you're losing 32 bits of data so the upper 32 bits will simply be stripped and lost.

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
mvdwerve
  • 78
  • 7
  • So, I have to use dynamic_cast to receive exception at runtime if number of days is behind DWORD scope and have normal work if it is inside DWORD? – Arkady Jun 28 '14 at 14:59
  • 1
    @Arkady no, `dynamic_cast` doesn't do that. Are you expecting "number of days" to exceed 4 billion? – Drew Dormann Jun 28 '14 at 15:04
  • @DrewDormann, I guess (qwCurResult - SomeULONGLONGMoment) / _DAY will never goes higher 4 billiions, but there exist potential possibility of that, and I want to understand how to handle it right way. – Arkady Jun 28 '14 at 15:07
  • @Arkady Well, the thing is, if you need a DWORD you cannot prevent the possible loss of data. However, you can check if data has been lost by comparing the DWORD to the original outcome. If you store it as a ULONGLONG result first, eg. ULONGLONG result = (qwCurResult - SomeULONGLONGMoment) / _DAY; and then DWORD days = (DWORD) result;. If data has been lost, days != result, otherwise they will be equal. – mvdwerve Jun 28 '14 at 15:18
  • This is a bug waiting to happen. ULONGLONG is not a good type for a difference in timestamps because the difference may be negative. All you need is a little clock skew or for someone to set the clock and your event will appear to have happened more than 4 billion days ago. – arx Jun 28 '14 at 15:30
  • @arx Well actually, qwCurResult cannot become negative, so as long as SomeULONGLONGMoment is smaller than the qwCurResult there really is no problem. – mvdwerve Jun 28 '14 at 15:35
  • @arx is correct. `SomeULONGLONGMoment` could easily become larger through more bad coding or someone just messing with the system's clock. The entire code sample is a wobbly 3-legged stool, metaphorically. The defines should also be `const` integer types. The symbol names shouldn't begin with underscores. The best think Arkady can do is keep the warnings enabled and fix each one. – Drew Dormann Jun 28 '14 at 15:37
  • @DrewDormann there exist check, that this part of code can be executed just if SomeULONGLONGMoment is smaller then qwCurResult, still I guess there have to be another code style that will allow to avoid such problems as another code style result. – Arkady Jun 28 '14 at 15:55
  • @mvdwerve i guess if I will first get (qwCurResult - SomeULONGLONGMoment) / _DAY and convert it to DWORD, even if I will lose data, checking you supposed will be fine, because I have to do there same conversion and will also lose data. And I will compare two DWORDs, where both lost data. I guess checking have to be at ULONGLONG types, I mean - I will need to convert DWORD to ULONGLONG, and then check. Still, good idea, thank you! – Arkady Jun 28 '14 at 16:04