क्या std :: chrono :: वर्ष भंडारण वास्तव में कम से कम 17 बिट है?


14

से cppreference

std::chrono::years (since C++20) duration</*signed integer type of at least 17 bits*/, std::ratio<31556952>>

का उपयोग करते हुए libc++, यह लगता है कि 16 बिट्स पर हस्ताक्षर किए गए अंडरलाइनिंग का भंडारण std::chrono::yearsहै ।short

std::chrono::years( 30797 )        // yields  32767/01/01
std::chrono::years( 30797 ) + 365d // yields -32768/01/01 apparently UB

वहाँ cppreference या कुछ और पर एक टाइपो है?

उदाहरण:

#include <fmt/format.h>
#include <chrono>

template <>
struct fmt::formatter<std::chrono::year_month_day> {
  char presentation = 'F';

  constexpr auto parse(format_parse_context& ctx) {
    auto it = ctx.begin(), end = ctx.end();
    if (it != end && *it == 'F') presentation = *it++;

#   ifdef __exception
    if (it != end && *it != '}') {
      throw format_error("invalid format");
    }
#   endif

    return it;
  }

  template <typename FormatContext>
  auto format(const std::chrono::year_month_day& ymd, FormatContext& ctx) {
    int year(ymd.year() );
    unsigned month(ymd.month() );
    unsigned day(ymd.day() );
    return format_to(
        ctx.out(),
        "{:#6}/{:#02}/{:#02}",
        year, month, day);
  }
};

using days = std::chrono::duration<int32_t, std::ratio<86400> >;
using sys_day = std::chrono::time_point<std::chrono::system_clock, std::chrono::duration<int32_t, std::ratio<86400> >>;

template<typename D>
using sys_time = std::chrono::time_point<std::chrono::system_clock, D>;
using sys_day2 = sys_time<days>;

int main()
{
  auto a = std::chrono::year_month_day( 
    sys_day( 
      std::chrono::floor<days>(
        std::chrono::hours( (1<<23) - 1 ) 
      )
    )
  );

  auto b = std::chrono::year_month_day( 
    sys_day( 
      std::chrono::floor<days>(
        std::chrono::minutes( (1l<<29) - 1 ) 
      )
    )
  );

  auto c = std::chrono::year_month_day( 
    sys_day( 
      std::chrono::floor<days>(
        std::chrono::seconds( (1l<<35) - 1 ) 
      )
    )
  );

  auto e = std::chrono::year_month_day( 
    sys_day( 
      std::chrono::floor<days>(
        std::chrono::days( (1<<25) - 1 ) 
      )
    )
  );

  auto f = std::chrono::year_month_day( 
    sys_day( 
      std::chrono::floor<days>(
        std::chrono::weeks( (1<<22) - 1 ) 
      )
    )
  );

  auto g = std::chrono::year_month_day( 
    sys_day( 
      std::chrono::floor<days>(
        std::chrono::months( (1<<20) - 1 ) 
      )
    )
  );

  auto h = std::chrono::year_month_day( 
    sys_day( 
      std::chrono::floor<days>(
        std::chrono::years( 30797 ) // 0x7FFF - 1970
      )
    )
  );

  auto i = std::chrono::year_month_day( 
    sys_day( 
      std::chrono::floor<days>(
        std::chrono::years( 30797 ) // 0x7FFF - 1970
      ) + std::chrono::days(365)
    )
  );

  fmt::print("Calendar limit by duration's underlining storage:\n"
             "23 bit hour       : {:F}\n"
             "29 bit minute     : {:F}\n"
             "35 bit second     : {:F}\n"
             "25 bit days       : {:F}\n"
             "22 bit week       : {:F}\n"
             "20 bit month      : {:F}\n"
             "16? bit year      : {:F}\n"
             "16? bit year+365d : {:F}\n"
             , a, b, c, e, f, g, h, i);
}

[ गॉडबॉल्ट लिंक ]


2
yearरेंज: eel.is/c++draft/time.cal.year#members-19 years रेंज: eel.is/c++draft/time.synyearनागरिक वर्ष का "नाम" है और इसके लिए 16 बिट्स की आवश्यकता होती है। yearsक्रोनो अवधि है, ए के रूप में एक ही बात नहीं है year। एक दो को घटा सकता है yearऔर परिणाम में टाइप हो सकता है yearsyearsके परिणाम को धारण करने में सक्षम होना आवश्यक है year::max() - year::min()
हावर्ड हिनांट

1
std::chrono::years( 30797 ) + 365dसंकलन नहीं है।
हावर्ड हिनान्ट

1
years{30797} + days{365}216s की इकाइयों के साथ 204528013 का परिणाम है।
हावर्ड हिनांट

1
यह सिर्फ दो अवधि जोड़ा जा रहा है। निषिद्ध करने का मतलब होगा निषिद्ध करना hours{2} + seconds{5}
हावर्ड हिनांट

4
मेरा अनुमान है कि आप अवधि प्रकार के साथ पंचांग घटकों भ्रमित कर रहे हैं, क्योंकि वे है है इस तरह के समान नाम है। यहाँ एक सामान्य नियम है: durationनाम बहुवचन हैं: years, months, days। पंचांग घटकों के नाम विलक्षण हैं: year, month, dayyear{30797} + day{365}एक संकलन-समय त्रुटि है। year{2020}इस साल है। years{2020}2020 की अवधि लंबी है।
हावर्ड हिनांट

जवाबों:


8

Cppreference लेख सही है । यदि libc ++ एक छोटे प्रकार का उपयोग करता है तो यह libc ++ में बग प्रतीत होता है।


लेकिन एक और जोड़ना wordजो शायद ही बमुश्किल इस्तेमाल किया जाता है, year_month_dayवैक्टर को अनावश्यक रूप से नहीं खींचेगा? क्या at least 17 bitsइसे आदर्श पाठ के रूप में नहीं गिना जा सकता है?
सैंडहॉर्न

3
@ सैंडथॉर्न year_month_dayहोते हैं year, नहीं yearsyear16-बिट का प्रतिनिधित्व करने की आवश्यकता नहीं है, हालांकि इस प्रकार shortका उपयोग प्रदर्शनी के रूप में किया जाता है। ओटीओएच, yearsपरिभाषा में 17 बिट्स का हिस्सा आदर्श है क्योंकि यह केवल प्रदर्शनी के रूप में चिह्नित नहीं है। और स्पष्ट रूप से, यह कहते हुए कि यह कम से कम 17 बिट्स है और तब इसकी आवश्यकता नहीं है व्यर्थ है।
एंड्रे सेमशेव

1
आह वास्तव yearमें year_month_dayप्रतीत होता है int। => ऑपरेटर इंट मुझे लगता है कि यह at least 17 bits yearsकार्यान्वयन का समर्थन करता है।
सैंडर्थन

क्या आप अपने उत्तर को संपादित करेंगे? यह पता चला std :: chrono :: वर्ष है वास्तव में पूर्णांक और std :: chrono :: साल है 32767 पर अधिकतम arbitarily ..
sandthorn

@sandthorn उत्तर सही है, मुझे नहीं पता कि मुझे इसे संपादित करने की आवश्यकता क्यों होगी।
एंड्रे सेमशेव

4

मैं https://godbolt.org/z/SNivyp piece by piece पर उदाहरण को तोड़ रहा हूं :

  auto a = std::chrono::year_month_day( 
    sys_days( 
      std::chrono::floor<days>(
        std::chrono::years(0) 
        + std::chrono::days( 365 )
      )
    )
  );

सरलीकरण और ग्रहण using namespace std::chronoकरना दायरे में है:

year_month_day a = sys_days{floor<days>(years{0} + days{365})};

उप अभिव्यक्ति years{0}एक है durationएक साथ periodकरने के लिए बराबर ratio<31'556'952>है और एक मूल्य के बराबर 0। ध्यान दें कि years{1}, फ्लोटिंग-पॉइंट के रूप में व्यक्त किया गया days, बिल्कुल 365.2425 है। यह नागरिक वर्ष की औसत लंबाई है।

उप अभिव्यक्ति days{365}एक है durationएक साथ periodकरने के लिए बराबर ratio<86'400>है और एक मूल्य के बराबर 365

उप अभिव्यक्ति years{0} + days{365}एक है durationएक साथ periodकरने के लिए बराबर ratio<216>है और एक मूल्य के बराबर 146'000। यह पहली खोज द्वारा बनाई है common_type_tकी ratio<31'556'952>और ratio<86'400>जो GCD (31'556'952, 86'400), या 216. पुस्तकालय पहले धर्मान्तरित इस आम इकाई के लिए दोनों ऑपरेंड है, और फिर आम इकाई में इसके अलावा करता है।

years{0}216 की अवधि वाली इकाइयों में बदलने के लिए 0 को 146'097 से गुणा करना चाहिए। यह एक बहुत महत्वपूर्ण बिंदु होता है। केवल 32 बिट्स के साथ किए जाने पर यह रूपांतरण आसानी से अतिप्रवाह का कारण बन सकता है।

<एक तरफ>

यदि इस बिंदु पर आप भ्रमित महसूस करते हैं, तो यह इसलिए है क्योंकि कोड की संभावना एक कैलेंड्रिकल संगणना का इरादा रखती है , लेकिन वास्तव में कालानुक्रमिक गणना कर रही है । कैलेंड्रिकल संगणनाएँ कैलेंडरों के साथ अभिकलन हैं।

कैलेंडर्स में सभी प्रकार की अनियमितताएं होती हैं, जैसे कि महीनों और वर्षों के दिनों में अलग-अलग भौतिक लंबाई होना। एक कैलेंड्रिकल अभिकलन इन अनियमितताओं को ध्यान में रखता है।

एक कालानुक्रमिक संगणना निश्चित इकाइयों के साथ काम करती है, और सिर्फ कैलेंडर के संबंध में संख्याओं के बिना क्रैंक करती है। यदि आप ग्रेगोरियन कैलेंडर, जूलियन कैलेंडर, हिंदू कैलेंडर, चीनी कैलेंडर आदि का उपयोग करते हैं, तो कालानुक्रमिक संगणना परवाह नहीं करता है।

</ एक तरफ>

अगला हम अपने लेने 146000[216]sकी अवधि और एक के साथ एक अवधि के लिए परिवर्तित periodकी ratio<86'400>(जो एक प्रकार उर्फ नामक days)। फ़ंक्शन floor<days>()यह रूपांतरण करता है और परिणाम 365[86400]sबस, या अधिक है 365d

अगला कदम एक लेता है durationऔर इसे एक में परिवर्तित करता है time_point। के प्रकार time_pointहै time_point<system_clock, days>जो एक प्रकार उर्फ नामक sys_days। यह केवल की गिनती है daysके बाद से system_clockयुग है, जो 1970-01-01 00:00:00 यूटीसी है, छलांग सेकंड को छोड़कर।

अंत में मूल्य के साथ sys_daysपरिवर्तित किया जाता है ।year_month_day1971-01-01

इस गणना को करने का एक सरल तरीका है:

year_month_day a = sys_days{} + days{365};

इस तरह की गणना पर विचार करें:

year_month_day j = sys_days{floor<days>(years{14699} + days{0})};

इसका परिणाम दिनांक में है 16668-12-31। जो शायद एक दिन पहले की तुलना में आप उम्मीद कर रहे थे ((14699 + 1970) -01-01)। उपप्रकार years{14699} + days{0}अब है 2'147'479'803[216]s:। ध्यान दें कि रन-टाइम वैल्यू लगभग INT_MAX( 2'147'483'647) है, और यह repदोनों की अंतर्निहित है yearsऔर daysहै int

वास्तव में अगर आप years{14700}की इकाइयों में परिवर्तित करने के [216]sलिए अतिप्रवाह मिलता है -2'147'341'396[216]s:।

इसे ठीक करने के लिए, एक कैलेंड्रिकल संगणना पर जाएँ:

year_month_day j = (1970y + years{14700})/1/1;

Https://godbolt.org/z/SNivyp के सभी परिणाम जो जोड़ रहे हैं yearsऔर उनके daysलिए एक मूल्य का उपयोग करना years14699 से अधिक है, intअतिप्रवाह का अनुभव कर रहे हैं ।

यदि कोई वास्तव में कालानुक्रमिक संगणना yearsऔर daysइस तरह से करना चाहता है , तो 64 बिट अंकगणित का उपयोग करना बुद्धिमान होगा। यह संगणना में 32 बिट्स से अधिक के उपयोग के yearsसाथ इकाइयों में परिवर्तित करके पूरा किया जा सकता है rep। उदाहरण के लिए:

years{14700} + 0s + days{0}

जोड़ कर 0sकरने के लिए years, ( secondsकम से कम 35 बिट्स होना आवश्यक है), तो common_type rep64 बिट्स के लिए सबसे पहले इसके लिए मजबूर किया जाता है ( years{14700} + 0s) और 64 बिट्स को जोड़ते समय में जारी है days{0}:

463'887'194'400s == 14700 * 365.2425 * 86400

फिर भी मध्यवर्ती अतिप्रवाह (इस सीमा पर) से बचने का एक और तरीका यह है कि अधिक जोड़ने से पहले परिशुद्धता को काट दिया yearsजाए :daysdays

year_month_day j = sys_days{floor<days>(years{14700})} + days{0};

jमूल्य है 16669-12-31। यह समस्या से बचा जाता है क्योंकि अब [216]sइकाई पहले स्थान पर कभी नहीं बनी है। और हम कभी भी सीमा के करीब नहीं जाते हैं years, daysया year

यद्यपि यदि आप उम्मीद कर रहे थे 16700-01-01, तो आपको अभी भी एक समस्या है, और इसे ठीक करने का तरीका इसके बजाय एक कैलेंड्रिकल संगणना करना है:

year_month_day j = (1970y + years{14700})/1/1;

1
महान व्याख्या। मैं कालानुक्रमिक गणना के बारे में चिंतित हूं। यदि मैं years{14700} + 0s + days{0}एक कोडबेस में देखता हूं , तो मुझे नहीं पता होगा कि 0sवहां क्या हो रहा है और यह कितना महत्वपूर्ण है। क्या कोई वैकल्पिक, शायद अधिक स्पष्ट तरीका है? क्या कुछ duration_cast<seconds>(years{14700}) + days{0}बेहतर होगा?
बोलोव

duration_castयह बदतर होगा क्योंकि यह duration_castगैर-ट्रंचिंग रूपांतरणों के लिए उपयोग करने के लिए खराब रूप है । ट्रंकिंग रूपांतरण तर्क त्रुटियों का एक स्रोत हो सकता है, और यह केवल "बड़ा हथौड़ा" का उपयोग करने के लिए सबसे अच्छा है जब आपको इसकी आवश्यकता होती है, ताकि आप अपने कोड में ट्रंकटिंग रूपांतरणों को आसानी से देख सकें।
हावर्ड हिनांट

1
कोई कस्टम अवधि बना सकता है: use llyears = duration<long long, years::period>;और फिर इसके बजाय इसका उपयोग करें। लेकिन शायद सबसे अच्छी बात यह है कि आप जो हासिल करने की कोशिश कर रहे हैं उसके बारे में सोचें और सवाल करें कि क्या आप इसके बारे में सही तरीके से जा रहे हैं। उदाहरण के लिए, क्या आपको वास्तव में एक समय के पैमाने पर दिन-सटीकता की आवश्यकता है जो 10 हजार साल है? सिविल कैलेंडर 4 हजार वर्षों में केवल 1 दिन के लिए सटीक है। शायद एक अस्थायी बिंदु सहस्राब्दी एक बेहतर इकाई होगी?
हावर्ड हिनान्ट

स्पष्टता: सिविल कैलेंडर के क्रोनो का मॉडलिंग -32767/1/1 से 32767/12/31 की सीमा में सटीक है। सौर प्रणाली के मॉडलिंग के संबंध में नागरिक कैलेंडर की सटीकता 4 हजार वर्षों में केवल 1 दिन है।
हावर्ड हिनान्ट

1
यह वास्तव में उपयोग के मामले पर निर्भर करेगा और मैं वर्तमान में जोड़ने के लिए एक प्रेरित उपयोग के मामले की परेशानी सोच आ रही हैं yearsऔर days। यह वस्तुतः 365.2425 दिनों के कुछ कई दिनों के कुछ अभिन्न संख्या को जोड़ रहा है। आम तौर पर यदि आप महीनों या वर्षों के क्रम पर कालानुक्रमिक गणना करना चाहते हैं, तो यह कुछ भौतिकी या जीव विज्ञान के मॉडल के लिए है। शायद अलग अलग तरीकों पर इस पोस्ट को जोड़ने के लिए monthsकरने के लिए system_clock::time_point: संगणना के दो प्रकार के बीच अंतर को स्पष्ट करने में मदद मिलेगी stackoverflow.com/a/43018120/576911
हावर्ड Hinnant
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.