計算毫秒級的時間差算是一個常見的需求吧...
手頭上是windows編程的項目,所以首先就想到的是GetTickCount(),但MSDN上這麼說:
寫個程序試一下吧:
1 #include <stdio.h> 2 #include <windows.h> 3 4 int main(void) 5 { 6 DWORD dwLastTime = GetTickCount(); 7 for (int i = 0; i != 10; ++i) 8 { 9 DWORD dwCurrentTime = GetTickCount(); 10 printf("GetTickCount = %ldms TimeDiff = %ldms\n", dwCurrentTime, dwCurrentTime - dwLastTime); 11 dwLastTime = dwCurrentTime; 12 Sleep(500); 13 } 14 return 0; 15 } View Code
可以看到,算了10次,每次偏差一般都有1ms,更有甚者,達到15ms,跟MSDN裡說的實際精度一樣。
所以,用GetTickCount()計算毫秒級的時間差是不靠譜的!
那下面,如何滿足我們的需求呢?
需求1:計算毫秒級別的時間差。
需求2:返回值最好是unsigned long級別的,以便與現有代碼保持兼容。
解決方案1:
clock_t clock(void);
這個函數返回的是從程序啟動到當前時刻所經歷的CPU時鐘周期數。將這個函數封裝一下即可:
1 #include <ctime> 2 3 ULONG GetTickCountClock() 4 { 5 return (ULONG)((LONGLONG)clock() * 1000 / CLOCKS_PER_SEC); 6 }
測試結果:
解決方案2:
SYSTEMTIME FILETIME
通過SYSTEMTIME和FILETIME,我們可以得到距離1601年1月1日凌晨所經歷的時間,單位是100納秒。
這個時間肯定是足夠精確了,但是得到的數值是一個LONGLONG,沒關系,我們可以用這個時間來校准原生的GetTickCount()。
1 ULONG GetTickCountCalibrate() 2 { 3 static ULONG s_ulFirstCallTick = 0; 4 static LONGLONG s_ullFirstCallTickMS = 0; 5 6 SYSTEMTIME systemtime; 7 FILETIME filetime; 8 GetLocalTime(&systemtime); 9 SystemTimeToFileTime(&systemtime, &filetime); 10 LARGE_INTEGER liCurrentTime; 11 liCurrentTime.HighPart = filetime.dwHighDateTime; 12 liCurrentTime.LowPart = filetime.dwLowDateTime; 13 LONGLONG llCurrentTimeMS = liCurrentTime.QuadPart / 10000; 14 15 if (s_ulFirstCallTick == 0) 16 { 17 s_ulFirstCallTick = GetTickCount(); 18 } 19 if (s_ullFirstCallTickMS == 0) 20 { 21 s_ullFirstCallTickMS = llCurrentTimeMS; 22 } 23 24 return s_ulFirstCallTick + (ULONG)(llCurrentTimeMS - s_ullFirstCallTickMS); 25 }
測試結果:
精度比較
每隔50ms獲取一次當前時刻,對比TimeDiff與50之間的差距,統計1000次:
1 #include <math.h> 2 3 int main(void) 4 { 5 int nMaxDeviation = 0; 6 int nMinDeviation = 99; 7 int nSumDeviation = 0; 8 9 DWORD dwLastTime = GetTickCountCalibrate(); 10 Sleep(50); 11 12 for (int i = 0; i != 1000; ++i) 13 { 14 DWORD dwCurrentTime = GetTickCountCalibrate(); 15 int nDeviation= abs(dwCurrentTime - dwLastTime - 50); 16 nMaxDeviation = nDeviation > nMaxDeviation ? nDeviation : nMaxDeviation; 17 nMinDeviation = nDeviation < nMinDeviation ? nDeviation : nMinDeviation; 18 nSumDeviation += nDeviation; 19 dwLastTime = dwCurrentTime; 20 Sleep(50); 21 } 22 printf("nMaxDeviation = %2dms, nMinDeviation = %dms, nSumDeviation = %4dms, AverDeviation = %.3fms\n", 23 nMaxDeviation, nMinDeviation, nSumDeviation, nSumDeviation / 1000.0f); 24 25 return 0; 26 } View Code比較GetTickCount、GetTickCountClock、GetTickCountCalibrate的精度如下:
GetTickCount nMaxDeviation = 13ms, nMinDeviation = 3ms, nSumDeviation = 5079ms, AverDeviation = 5.079ms GetTickCountClock nMaxDeviation = 2ms, nMinDeviation = 0ms, nSumDeviation = 4ms, AverDeviation = 0.004ms GetTickCountCalibrate nMaxDeviation = 1ms, nMinDeviation = 0ms, nSumDeviation = 3ms, AverDeviation = 0.003ms
可以看到,原生的GetTickCount誤差過大,最大誤差13ms,平均誤差5ms,肯定無法滿足毫秒級的計時需求。
GetTickCountClock與GetTickCountCalibrate在精度上相差無幾,都可以滿足毫秒級的計時需求。
區別在於,GetTickCountClock是從當前程序運行開始計時,GetTickCountCalibrate是從系統啟動開始計時。
有關溢出
4個字節的ULONG最大值是4294967296ms,也就是49.7天,超過這個值就會溢出。
1秒=1000毫秒,所以,你的2分54秒72毫秒其實就是2'54.072",這樣就可以計算了,你的結果是:2.993秒。
我明白你的意思了,你是因為表中數據無法計算是吧。
如果解決你這個問題有辦法是這樣的,比如,你2分54秒72在A1單元格,2分51秒79 在B1單元格,你現在要在C1中得到差:2.993秒。是不是這樣?
如果是你的操作應該是這樣的。先用分列操作把2分54秒72這樣的數據分成三列,分列操作進行兩次,分隔符為其他,寫上漢字分和秒,同樣操作對另一組數據,當這個數據中間沒有空列的時候,用插入的方式解決。這個操作完成後,A1為2,B1為54,C1為72,D1為2,E1為51,F1為79,然後你在G1上輸入公式進行減運算。比如:=A1-D1&"分"&(B1+C1/1000)-(E1+F1/1000)&"秒"
公式顯示的結果是:0分2.993秒
進行公式拖動填充。
希望你能看得明白。
如果你還需要表格恢復到以前的樣子,接下來的操作如下 。
復制G1列,在G列選擇性粘貼,數值,確定。在A1前和E1(兩個分鐘數前)各插入一列,在A1列中輸入公式:=B1&"分"&C1&"秒"&D1,
E1列中輸入公式:=F1&"分"&G1&"秒"&H1
分別拖動進行填充,然後復制選擇性粘貼數值,以去掉剛才編輯的公式,刪除表中是BCDFGH六列,這樣表格就恢復成原來的樣子了。
日期相減計算出多少天。時間相減計算出秒。 毫秒相減再計算出毫秒。最後相加。
還有一種方法。把兩個時間都轉成數字形,再相減。