Алгоритмы, которые вы показываете, очень хороши.На моей платформе (clang ++ -O3) они создают объектный код без ветвей (конвейерные конвейеры) и без доступа к удаленной памяти (отсутствует кеш).В паре существует диапазон действия от -4800-03-01 до миллионов лет в будущем (большой диапазон).Во всем этом диапазоне они моделируют григорианский календарь.
Вот несколько альтернативных алгоритмов , которые очень похожи.Одно из отличий состоит в том, что у вас есть эпоха 19-01-01-01, а у тех, что я представляю, эпоха 1970-01-01.Однако очень легко отрегулировать эпоху по разности этих эпох (25569 дней), как показано ниже:
constexpr
std::tuple<int, unsigned, unsigned>
civil_from_days(int z) noexcept
{
static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer");
static_assert(std::numeric_limits<int>::digits >= 20,
"This algorithm has not been ported to a 16 bit signed integer");
z += 719468 - 25569;
const int era = (z >= 0 ? z : z - 146096) / 146097;
const unsigned doe = static_cast<unsigned>(z - era * 146097); // [0, 146096]
const unsigned yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399]
const int y = static_cast<int>(yoe) + era * 400;
const unsigned doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365]
const unsigned mp = (5*doy + 2)/153; // [0, 11]
const unsigned d = doy - (153*mp+2)/5 + 1; // [1, 31]
const unsigned m = mp + (mp < 10 ? 3 : -9); // [1, 12]
return std::tuple<int, unsigned, unsigned>(y + (m <= 2), m, d);
}
constexpr
int
days_from_civil(int y, unsigned m, unsigned d) noexcept
{
static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer");
static_assert(std::numeric_limits<int>::digits >= 20,
"This algorithm has not been ported to a 16 bit signed integer");
y -= m <= 2;
const int era = (y >= 0 ? y : y-399) / 400;
const unsigned yoe = static_cast<unsigned>(y - era * 400); // [0, 399]
const unsigned doy = (153*(m + (m > 2 ? -3 : 9)) + 2)/5 + d-1; // [0, 365]
const unsigned doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096]
return era * 146097 + static_cast<int>(doe) - (719468 - 25569);
}
Эти алгоритмы действительны в течение миллионов лет как вперед, так и назад (в том числе до -4800)-03-01).Хотя этот дополнительный диапазон не принесет вам большой выгоды, потому что григорианский календарь даже не начинался до 1582-10-15.
Я скомпилировал обе пары алгоритмов в macOS, используя clang++ -O3 -S
, и набор, который у меня естьнемного меньший объектный код (около 10%).Несмотря на то, что они все такие маленькие, без ветвей и без кеш-памяти, попытка проверить эту выгоду путем измерения производительности была бы сложной задачей.
Я не нахожу читаемость одного набора выше, чем другого,Однако эта пара алгоритмов имеет раздражающе исчерпывающий вывод для тех, кому интересно, как работают эти алгоритмы, и модульные тесты, чтобы убедиться, что алгоритмы работают в диапазоне +/- 1 миллион лет.
Можно достичь очень незначительной производительности в вышеупомянутых алгоритмах, ограничив диапазон действия [2000-03-01, 2400-02-29], установив const int era = 5
в обоих алгоритмах.Я не тестировал производительность этой опции.Я ожидал бы, что такое усиление будет в уровне шума.
Или может быть какое-то незначительное преимущество в производительности, ограничивая диапазон от [0000-03-01, миллионы лет вперед], не учитывая отрицательные значенияera
:
В civil_from_days
:
const int era = z / 146097;
В days_from_civil
:
const int era = y / 400;