Dari cppreference
std::chrono::years (since C++20) duration</*signed integer type of at least 17 bits*/, std::ratio<31556952>>
Menggunakan libc++
, tampaknya penyimpanan garis bawah std::chrono::years
adalah short
yang ditandatangani 16 bit .
std::chrono::years( 30797 ) // yields 32767/01/01
std::chrono::years( 30797 ) + 365d // yields -32768/01/01 apparently UB
Apakah ada kesalahan ketik pada cppreference atau yang lainnya?
Contoh:
#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);
}
[ Tautan Godbolt ]
year
range: eel.is/c++draft/time.cal.year#members-19years
range: eel.is/c++draft/time.syn .year
adalah "nama" tahun sipil dan membutuhkan 16 bit.years
adalah durasi chrono, bukan hal yang sama dengan ayear
. Satu dapat mengurangi duayear
dan hasilnya memiliki tipeyears
.years
diperlukan untuk dapat memegang hasilyear::max() - year::min()
.std::chrono::years( 30797 ) + 365d
tidak dikompilasi.years{30797} + days{365}
adalah 204528013 dengan unit 216-an.hours{2} + seconds{5}
.duration
nama adalah jamak:years
,months
,days
. Nama komponen penanggalan adalah tunggal:year
,month
,day
.year{30797} + day{365}
adalah kesalahan waktu kompilasi.year{2020}
tahun ini.years{2020}
berdurasi 2020 tahun.Jawaban:
Artikel cppreference benar . Jika libc ++ menggunakan tipe yang lebih kecil maka ini tampaknya menjadi bug di libc ++.
sumber
word
yang mungkin jarang digunakan tidak akan menjadi bulkingyear_month_day
vektor yang tidak perlu? Mungkinkah ituat least 17 bits
tidak dihitung sebagai teks norminal?year_month_day
mengandungyear
, bukanyears
. Representasiyear
tidak harus 16-bit, meskipun tipeshort
digunakan sebagai eksposisi. OTOH, bagian 17 bit dalamyears
definisi adalah normatif karena tidak ditandai sebagai eksposisi saja. Dan terus terang, mengatakan bahwa itu setidaknya 17 bit dan kemudian tidak memerlukannya tidak ada artinya.year
inyear_month_day
tampaknyaint
memang. => operator int Saya pikir ini mendukungat least 17 bits
years
implementasi.Saya merinci contoh di https://godbolt.org/z/SNivyp sepotong demi sepotong:
Penyederhanaan dan asumsi
using namespace std::chrono
ada dalam ruang lingkup:Sub-ekspresi
years{0}
adalahduration
dengan denganperiod
sama denganratio<31'556'952>
dan nilai sama dengan0
. Perhatikan bahwayears{1}
, dinyatakan sebagai floating-pointdays
, tepat 365.2425. Ini adalah panjang rata - rata tahun sipil.Sub-ekspresi
days{365}
adalahduration
dengan denganperiod
sama denganratio<86'400>
dan nilai sama dengan365
.Sub-ekspresi
years{0} + days{365}
adalahduration
dengan denganperiod
sama denganratio<216>
dan nilai sama dengan146'000
. Hal ini dibentuk oleh temuan pertamacommon_type_t
dariratio<31'556'952>
danratio<86'400>
yang merupakan GCD (31'556'952, 86'400), atau 216. perpustakaan pertama bertobat kedua operan unit umum ini, dan kemudian melakukan penambahan di unit umum.Untuk mengkonversi
years{0}
ke unit dengan periode 216s seseorang harus mengalikan 0 dengan 146'097. Ini kebetulan menjadi poin yang sangat penting. Konversi ini dapat dengan mudah menyebabkan overflow bila dilakukan dengan hanya 32 bit.<samping>
Jika pada saat ini Anda merasa bingung, itu karena kode tersebut kemungkinan bermaksud perhitungan kalender , tetapi sebenarnya melakukan perhitungan kronologis . Perhitungan kalender adalah perhitungan dengan kalender.
Kalender memiliki segala macam penyimpangan, seperti bulan dan tahun yang memiliki panjang fisik yang berbeda dalam hal hari. Perhitungan kalender memperhitungkan penyimpangan ini.
Perhitungan kronologis bekerja dengan unit-unit tetap, dan hanya menghitung angka tanpa memperhatikan kalender. Perhitungan kronologis tidak peduli jika Anda menggunakan kalender Gregorian, kalender Julian, kalender Hindu, kalender Cina, dll.
</aside>
Berikutnya kita mengambil kami
146000[216]s
durasi dan mengubahnya menjadi durasi denganperiod
dariratio<86'400>
(yang memiliki jenis-alias bernamadays
). Fungsifloor<days>()
melakukan konversi ini dan hasilnya adalah365[86400]s
, atau lebih sederhana, adil365d
.Langkah selanjutnya mengambil
duration
dan mengubahnya menjadi atime_point
. Jenistime_point
istime_point<system_clock, days>
yang memiliki nama aliassys_days
. Ini hanyalah hitungandays
sejaksystem_clock
zaman, yaitu 1970-01-01 00:00:00 UTC, tidak termasuk detik kabisat.Akhirnya
sys_days
dikonversi menjadiyear_month_day
dengan nilai1971-01-01
.Cara sederhana untuk melakukan perhitungan ini adalah:
Pertimbangkan perhitungan serupa ini:
Ini menghasilkan tanggal
16668-12-31
. Yang mungkin sehari lebih awal dari yang Anda harapkan ((14699 + 1970) -01-01). Subexpression yangyears{14699} + days{0}
sekarang:2'147'479'803[216]s
. Perhatikan bahwa nilai run-time mendekatiINT_MAX
(2'147'483'647
), dan bahwa yang mendasarirep
keduanyayears
dandays
adalahint
.Memang jika Anda mengkonversi
years{14700}
ke unit[216]s
Anda mendapatkan overflow:-2'147'341'396[216]s
.Untuk mengatasinya, alihkan ke perhitungan kalender:
Semua hasil di https://godbolt.org/z/SNivyp yang menambahkan
years
dandays
dan menggunakan nilai untukyears
yang lebih besar dari 14.699 mengalamiint
meluap.Jika seseorang benar-benar ingin melakukan perhitungan kronologis dengan
years
dan dengandays
cara ini, maka akan lebih bijaksana untuk menggunakan aritmatika 64 bit. Ini dapat dicapai dengan mengkonversiyears
ke unit denganrep
menggunakan lebih besar dari 32 bit di awal perhitungan. Sebagai contoh:Dengan menambahkan
0s
keyears
, (seconds
harus memiliki minimal 35 bit), makacommon_type
rep
terpaksa 64 bit untuk penambahan pertama (years{14700} + 0s
) dan berlanjut di 64 bit saat menambahkandays{0}
:Namun cara lain untuk menghindari luapan menengah (di kisaran ini) adalah untuk memotong
years
untukdays
presisi sebelum menambahkan lebih banyakdays
:j
memiliki nilai16669-12-31
. Ini menghindari masalah karena sekarang[216]s
unit tidak pernah dibuat di tempat pertama. Dan kita bahkan tidak pernah mendekati batas untukyears
,days
atauyear
.Meskipun jika Anda mengharapkan
16700-01-01
, maka Anda masih memiliki masalah, dan cara untuk memperbaikinya adalah dengan melakukan perhitungan kalender sebagai gantinya:sumber
years{14700} + 0s + days{0}
dalam basis kode, saya tidak tahu apa0s
yang dilakukan di sana dan seberapa penting hal itu. Apakah ada cara alternatif, mungkin lebih eksplisit? Apakah sesuatu sepertiduration_cast<seconds>(years{14700}) + days{0}
lebih baik?duration_cast
akan lebih buruk karena itu bentuk yang buruk untuk digunakanduration_cast
untuk konversi yang tidak terpotong. Memotong konversi dapat menjadi sumber kesalahan logika, dan yang terbaik adalah hanya menggunakan "palu besar" saat Anda membutuhkannya, sehingga Anda dapat dengan mudah menemukan konversi pemotongan dalam kode Anda.use llyears = duration<long long, years::period>;
dan kemudian menggunakannya. Tetapi mungkin yang terbaik adalah memikirkan apa yang ingin Anda capai dan mempertanyakan apakah Anda melakukannya dengan cara yang benar. Misalnya apakah Anda benar-benar membutuhkan ketepatan hari dalam skala waktu 10 ribu tahun? Kalender sipil hanya akurat sekitar 1 hari dalam 4 ribu tahun. Mungkin milenium floating point akan menjadi unit yang lebih baik?years
dandays
. Ini secara harfiah menambahkan beberapa kelipatan dari 365,2425 hari ke beberapa hari yang tidak terpisahkan. Biasanya jika Anda ingin melakukan perhitungan kronologis pada urutan bulan atau tahun, itu adalah model beberapa fisika atau biologi. Mungkin posting ini tentang cara-cara yang berbeda untuk menambahkanmonths
kesystem_clock::time_point
akan membantu memperjelas perbedaan antara dua jenis perhitungan: stackoverflow.com/a/43018120/576911