Von cppreference
std::chrono::years (since C++20) duration</*signed integer type of at least 17 bits*/, std::ratio<31556952>>
Mit libc++
scheint es , die unterstreicht Speicherung std::chrono::years
heißt , short
die signiert ist 16 Bit .
std::chrono::years( 30797 ) // yields 32767/01/01
std::chrono::years( 30797 ) + 365d // yields -32768/01/01 apparently UB
Gibt es einen Tippfehler bei cppreference oder irgendetwas anderem?
Beispiel:
#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);
}
[ Godbolt Link ]
year
Bereich: eel.is/c++draft/time.cal.year#members-19years
Bereich: eel.is/c++draft/time.syn .year
ist der "Name" des Ziviljahres und erfordert 16 Bit.years
ist eine Chronodauer, nicht dasselbe wie eineyear
. Man kann zwei subtrahierenyear
und das Ergebnis hat Typyears
.years
ist erforderlich, um das Ergebnis von halten zu könnenyear::max() - year::min()
.std::chrono::years( 30797 ) + 365d
kompiliert nicht.years{30797} + days{365}
ist 204528013 mit Einheiten von 216s.hours{2} + seconds{5}
.duration
Namen sind Plural:years
,months
,days
. Kalendarische Komponentennamen sind Singular:year
,month
,day
.year{30797} + day{365}
ist ein Fehler bei der Kompilierung.year{2020}
ist dieses Jahr.years{2020}
ist eine Dauer von 2020 Jahren.Antworten:
Der Referenzartikel ist korrekt . Wenn libc ++ einen kleineren Typ verwendet, scheint dies ein Fehler in libc ++ zu sein.
quelle
word
, der wahrscheinlich kaum verwendet wird, würde nicht dazu führen, dassyear_month_day
Vektoren unnötig groß werden? Könnte dasat least 17 bits
nicht als normaler Text gezählt werden?year_month_day
enthältyear
, nichtyears
. Die Darstellung von mussyear
nicht 16-Bit sein, obwohl der Typshort
als Exposition verwendet wird. OTOH, der 17-Bit-Teil in deryears
Definition ist normativ, da er nicht nur als Exposition markiert ist. Und ehrlich gesagt ist es bedeutungslos zu sagen, dass es mindestens 17 Bit hat und es dann nicht benötigt.year
inyear_month_day
scheint in derint
Tat zu sein. => operator int Ich denke, dies unterstützt dieat least 17 bits
years
Implementierung.Ich zerlege das Beispiel unter https://godbolt.org/z/SNivyp Stück für Stück:
Vereinfachen und Annehmen
using namespace std::chrono
liegt im Geltungsbereich:Der Unterausdruck
years{0}
ist aduration
mit einerperiod
gleichratio<31'556'952>
und einem Wert gleich0
. Beachten Sie, dassyears{1}
, ausgedrückt als Gleitkommadays
, genau 365,2425 beträgt. Dies ist die durchschnittliche Länge des Ziviljahres.Der Unterausdruck
days{365}
ist einduration
mit einerperiod
gleichratio<86'400>
und einem Wert gleich365
.Der Unterausdruck
years{0} + days{365}
ist aduration
mit aperiod
gleichratio<216>
und einem Wert gleich146'000
. Dies wird gebildet, indem zuerst dascommon_type_t
vonratio<31'556'952>
undratio<86'400>
das GCD (31'556'952, 86'400) oder 216 gefunden wird. Die Bibliothek konvertiert zuerst beide Operanden in diese gemeinsame Einheit und führt dann die Addition in der gemeinsamen Einheit durch.Um
years{0}
in Einheiten mit einer Periode von 216s umzurechnen, muss man 0 mit 146'097 multiplizieren. Dies ist zufällig ein sehr wichtiger Punkt. Diese Konvertierung kann leicht zu einem Überlauf führen, wenn sie nur mit 32 Bit ausgeführt wird.<Abseite>
Wenn Sie sich zu diesem Zeitpunkt verwirrt fühlen, liegt dies daran, dass der Code wahrscheinlich eine kalendarische Berechnung beabsichtigt , aber tatsächlich eine chronologische Berechnung durchführt. Kalenderberechnungen sind Berechnungen mit Kalendern.
Kalender weisen alle Arten von Unregelmäßigkeiten auf, z. B. Monate und Jahre, die in Bezug auf Tage unterschiedlich lang sind. Eine kalendarische Berechnung berücksichtigt diese Unregelmäßigkeiten.
Eine chronologische Berechnung arbeitet mit festen Einheiten und dreht nur die Zahlen ohne Rücksicht auf Kalender aus. Eine chronologische Berechnung spielt keine Rolle, wenn Sie den Gregorianischen Kalender, den Julianischen Kalender, den Hindu-Kalender, den chinesischen Kalender usw. verwenden.
</ side>
Als nächstes nehmen wir unsere
146000[216]s
Dauer und konvertieren sie in eine Dauer mit einemperiod
vonratio<86'400>
(das einen Typ-Alias namens hatdays
). Die Funktionfloor<days>()
führt diese Konvertierung durch und das Ergebnis ist365[86400]s
oder einfacher365d
.Der nächste Schritt nimmt das
duration
und wandelt es in eintime_point
. Die Art dertime_point
isttime_point<system_clock, days>
hat einen Typalias namenssys_days
. Dies ist einfach eine Zählungdays
seit dersystem_clock
Epoche, die 1970-01-01 00:00:00 UTC ist, ohne Schaltsekunden.Endlich, das
sys_days
wird dasyear_month_day
mit dem Wert in a umgewandelt1971-01-01
.Eine einfachere Möglichkeit, diese Berechnung durchzuführen, ist:
Betrachten Sie diese ähnliche Berechnung:
Daraus ergibt sich das Datum
16668-12-31
. Das ist wahrscheinlich einen Tag früher als erwartet ((14699 + 1970) -01-01). Der Unterausdruckyears{14699} + days{0}
lautet jetzt :2'147'479'803[216]s
. Beachten Sie, dass sich der LaufzeitwertINT_MAX
(2'147'483'647
) nähert und dassrep
beide zugrunde liegenyears
unddays
istint
.In der Tat, wenn Sie konvertieren
years{14700}
in Einheiten von , erhalten[216]s
Sie einen Überlauf :-2'147'341'396[216]s
.Um dies zu beheben, wechseln Sie zu einer Kalenderberechnung:
Alle Ergebnisse unter https://godbolt.org/z/SNivyp , die
years
und hinzufügendays
einen Wert verwendenyears
, der größer als 14699 ist, werden angezeigtint
Überlauf auf.Wenn man wirklich chronologische Berechnungen mit
years
und aufdays
diese Weise durchführen möchte, ist es ratsam, 64-Bit-Arithmetik zu verwenden. Dies kann erreicht werden, indemyears
zurep
Beginn der Berechnung in Einheiten mit einer Verwendung von mehr als 32 Bit konvertiert wird . Zum Beispiel:Durch Hinzufügen
0s
zuyears
(seconds
muss mindestens 35 Bit haben) wird dascommon_type
rep
für die erste Addition (years{14700} + 0s
) auf 64 Bit gezwungen und beim Hinzufügen in 64 Bit fortgesetztdays{0}
:Eine weitere Möglichkeit, einen Zwischenüberlauf (in diesem Bereich) zu vermeiden, besteht darin
years
, diedays
Genauigkeit zu verringern , bevor weitere hinzugefügt werdendays
:j
hat den Wert16669-12-31
. Dies vermeidet das Problem, da das[216]s
Gerät jetzt überhaupt nicht mehr erstellt wird. Und wir bekommen nicht einmal nahe an der Grenze füryears
,days
oderyear
.Wenn Sie erwartet haben
16700-01-01
, haben Sie immer noch ein Problem, und die Möglichkeit, es zu korrigieren, besteht darin, stattdessen eine kalendarische Berechnung durchzuführen:quelle
years{14700} + 0s + days{0}
in einer Codebasis sehe, hätte ich keine Ahnung, was0s
dort tut und wie wichtig es ist. Gibt es einen alternativen, vielleicht expliziteren Weg? Wäre etwasduration_cast<seconds>(years{14700}) + days{0}
besser?duration_cast
wäre schlimmer, weil es eine schlechte Form ist, sieduration_cast
für nicht abschneidende Konvertierungen zu verwenden. Das Abschneiden von Konvertierungen kann zu logischen Fehlern führen. Verwenden Sie den "großen Hammer" am besten nur, wenn Sie ihn benötigen, damit Sie die abgeschnittenen Konvertierungen in Ihrem Code leicht erkennen können.use llyears = duration<long long, years::period>;
und diese stattdessen verwenden. Aber wahrscheinlich ist es das Beste, darüber nachzudenken, was Sie erreichen wollen, und sich zu fragen, ob Sie es richtig machen. Benötigen Sie zum Beispiel wirklich Tagesgenauigkeit auf einer Zeitskala von 10 Tausend Jahren? Der Zivilkalender ist nur auf ungefähr einen Tag in viertausend Jahren genau. Vielleicht wäre ein Gleitkomma-Jahrtausend eine bessere Einheit?years
und hinzuzufügendays
. Dies bedeutet buchstäblich ein Vielfaches von 365,2425 Tagen zu einer ganzzahligen Anzahl von Tagen. Wenn Sie eine chronologische Berechnung in der Größenordnung von Monaten oder Jahren durchführen möchten, müssen Sie normalerweise etwas Physik oder Biologie modellieren. Vielleicht ist dieser Beitrag auf den verschiedenen Möglichkeiten hinzuzufügen ,months
umsystem_clock::time_point
den Unterschied zwischen den beiden Arten von Berechnungen würde helfen , zu klären: stackoverflow.com/a/43018120/576911