2017-05-30 36 views
6

我正在嘗試最近的std::chrono api,我發現在64位Linux架構和gcc編譯器中,time_pointduration類無法以最大分辨率(納秒)處理操作系統的最大時間範圍。事實上,它似乎對這些類的存儲是一個64位的整數類型,相比timespectimeval其內部使用兩個64位整數,一個是秒,一個用於納秒:爲什麼std :: chrono :: time_point不足以存儲結構timespec?

#include <iostream> 
#include <chrono> 
#include <typeinfo> 
#include <time.h> 

using namespace std; 
using namespace std::chrono; 

int main() 
{ 
    cout << sizeof(time_point<nanoseconds>) << endl;      // 8 
    cout << sizeof(time_point<nanoseconds>::duration) << endl;    // 8 
    cout << sizeof(time_point<nanoseconds>::duration::rep) << endl;  // 8 
    cout << typeid(time_point<nanoseconds>::duration::rep).name() << endl; // l 
    cout << sizeof(struct timespec) << endl;        // 16 
    cout << sizeof(struct timeval) << endl;        // 16 
    return 0; 
} 

在64和Windows(MSVC2017 )情況非常相似:存儲類型也是64位整數。處理穩定(又稱單調)時鐘時,這不是問題,但存儲限制使得不同的API實現不適合存儲更大的日期和更長的時間跨度,從而爲類似於Y2K的錯誤創造了基礎。問題是否得到承認?是否有更好的實施或API改進計劃?

回答

12

這樣做是爲了讓您獲得最大的靈活性和緊湊的尺寸。如果你需要超精密的精度,你通常不需要很大的範圍。如果你需要非常大的範圍,你通常不需要非常高的精度。

例如,如果您以納秒運輸,您是否經常需要考慮超過+/- 292年?如果你需要考慮的範圍大於這個範圍,那麼很好的微秒會給你+/- 292 一千年年。

macOS system_clock實際上會返回微秒,而不是納秒。所以這個時鐘可以從1970年開始運行292年,直到它溢出。

Windows system_clock的精度爲100納秒單位,因此範圍爲+/- 29.2千年。

如果幾十萬年還不夠,請嘗試毫秒。現在您的範圍可達+/- 292 百萬年。

最後,如果你只是已經納秒的精度了超過兩百年,<chrono>允許您自定義存儲過:

using dnano = duration<double, nano>; 

這給你納秒存儲爲double。如果你的平臺支持128位的整數類型,你可以使用它:

using big_nano = duration<__int128_t, nano>; 

哎呀,如果你寫重載運營商timespec,你甚至可以用爲存儲(我不推薦雖然)。

你也可以達到比納秒更精細的精度,但是你會犧牲範圍。例如:

using picoseconds = duration<int64_t, pico>; 

這隻有+/- .292年(幾個月)的範圍。所以你必須小心。非常適合計時的事情,但如果你有一個能夠提供亞納秒精度的源時鐘。

有關<chrono>的更多信息,請查看this video

爲了創建,處理和存儲日期的範圍大於當前公曆的有效期,我創建了這個open-source date library,它使用日曆服務來擴展<chrono>庫。這個庫將年份存儲在一個有符號的16位整數中,因此具有+/- 32K年的範圍。它可以像這樣使用:

#include "date.h" 

int 
main() 
{ 
    using namespace std::chrono; 
    using namespace date; 
    system_clock::time_point now = sys_days{may/30/2017} + 19h + 40min + 10s; 
} 

更新

在問題下面的意見是問如何「正常化」 duration<int32_t, nano>到秒和納秒(然後秒添加到time_point)。

首先,我警惕餡納秒到32位。範圍僅略高於+/- 2秒。但這裏是我如何分離出單位是這樣的:

using ns = duration<int32_t, nano>; 
    auto n = ns::max(); 
    auto s = duration_cast<seconds>(n); 
    n -= s; 

注意,如果n爲正這僅適用。要正確處理負n,做的最好的事情是:

auto n = ns::max(); 
    auto s = floor<seconds>(n); 
    n -= s; 

std::floor與C++ 17引入的。如果您想要更早,您可以從herehere中獲取。

我偏愛上面的減法運算,因爲我只是覺得它更具可讀性。但是,這也可以(如果n非負):

auto s = duration_cast<seconds>(n); 
    n %= 1s; 

1s在C++ 14引入。在C++ 11中,您將不得不使用seconds{1}

一旦你有了秒(s),您可以添加到您的time_point

+0

你的回答讓有很大的意義,謝謝。是否有來標準化API在一些商品,例如,一個'time_point '和一個持續時間''一起使用?因此,例如,在這段時間內永遠不會有大於等於1秒的東西? – ceztko

+0

@ceztko:對不起,我不完全理解這個問題。如果以下內容沒有回答,你可以重新翻譯一下嗎? 'time_point +持續時間'會給你一個'time_point '(它會在2秒內溢出)。沒有內置溢出保護。但是,如果您抓取一個「safeint」庫,則可以使用它來獲取溢出保護。 –

+0

對不起,這個例子其實是錯誤的。我的意思是標準化一個'time_point '和一個'duration ':在這種情況下,我可能會將'duration '轉換爲秒數(以floor爲單位),然後求和到'time_point '並且將這個持續時間重寫爲一秒。只是想知道用API來做到這一點最流暢的方式。 – ceztko

0

std::chrono::nanosecondsstd::chrono::duration<some_t, std::nano>的類型別名,其中some_t是存儲空間至少爲64位的signed int。這仍然允許至少292年的納秒精度。

值得注意的是,具有此標準提及的這些特徵的唯一整體類型是int(| _fast | _least64_t家族。

你可以自由選擇一個更廣泛的類型來表示你的時間,如果你的實現提供了一個。您可以更自由地提供一個命名空間,其中包含一系列可以反映比例的typedef,並使用更寬的類型作爲表示。

相關問題