Дробный день вычисления года в C ++ 14 - PullRequest
6 голосов
/ 27 марта 2019

Я написал следующий код, используя библиотеку Говарда Хиннанта date.h, чтобы вычислить дробный день года текущего времени.Мне было интересно, есть ли более короткие способы сделать это, потому что мой код ощущается как избыточное количество вызовов std::chrono и date.Могу ли я напрямую рассчитать количество дробных дней с начала года (с точностью до микросекунды) и избежать моего двухэтапного подхода?

#include <iostream>
#include <chrono>
#include "date.h"

int main()
{
    // Get actual time.
    auto now = std::chrono::system_clock::now();

    // Get the number of days since start of the year.
    auto ymd = date::year_month_day( date::floor<date::days>(now) );
    auto ymd_ref = date::year{ymd.year()}/1/1;
    int days = (date::sys_days{ymd} - date::sys_days{ymd_ref}).count();

    // Get the fractional number of seconds of the day.
    auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now - date::floor<date::days>(now));
    double seconds_since_midnight = 1e-6*microseconds.count();

    // Get fractional day number.
    std::cout << "Fractional day of the year: " << days + seconds_since_midnight / 86400. << std::endl;

    return 0;
}

1 Ответ

6 голосов
/ 27 марта 2019

Хороший вопрос (проголосовал).

Думаю, сначала нам нужно определиться с правильным ответом. Вот ваш ответ, и в настоящее время единственным другим ответом является Matteo's . В демонстрационных целях я изменил оба ответа, чтобы заменить в «подделке сейчас», чтобы мы могли сравнить яблоки с яблоками:

using namespace std::chrono_literals;
auto now = date::sys_days{date::March/27/2019} + 0h +  32min + 22s + 123456us;

(примерно сейчас, когда я пишу это)

Код Чила дает:

Fractional day of the year: 85.0225

Код Маттео дает:

Fractional day of the year: 85.139978280740735

Они близко, но не настолько близко, чтобы оба считались правильными.

Код Маттео работает со "средними годами":

auto this_year = date::floor<date::years>(now);

Длина date::years составляет 365,2425 дней, что совершенно верно, если вы усредните все гражданские годы за 400-летний период. И работа со средней продолжительностью года может быть очень полезной, особенно когда речь идет о системах, которые не заботятся о созданных человеком календарях (например, физика или биология).

Я собираюсь догадаться, что из-за способа написания кода Чила он предпочел бы результат, который более точно относится к этому конкретному году. Поэтому приведенный ниже код является алгоритмом Чиля, в результате чего получается точно такой же результат, только чуть более эффективный и лаконичный.

// Get actual time.
auto now = std::chrono::system_clock::now();

// Get the number of days since start of the year.
auto sd = date::floor<date::days>(now);
auto ymd = date::year_month_day( sd );
auto ymd_ref = ymd.year()/1/1;
std::chrono::duration<double, date::days::period> days = sd - date::sys_days{ymd_ref};

// Get the fractional number of seconds of the day.
days += now - sd;

// Get fractional day number.
std::cout << "Fractional day of the year: " << days.count() << std::endl;

Первое, что я заметил, было то, что date::floor<date::days>(now) вычислялось в 3 местах, поэтому я вычисляю его один раз и сохраняю в sd.

Далее, поскольку окончательный ответ представляет собой двойное представление days, я позволю <chrono> выполнить эту работу за меня, сохранив ответ в duration<double, days>. Каждый раз, когда вы решаете конвертировать единицы, лучше позволить <chrono> сделать это за вас. Это, вероятно, не будет быстрее. Но это определенно не будет медленнее или неправильнее.

Теперь просто добавить дробный день к результату:

days += now - sd;

с любой точностью now (микросекунды или что-то еще). И результат теперь просто days.count().

Обновление

И еще немного времени, чтобы подумать ...

Я заметил, что с упрощенным кодом, приведенным выше, легче увидеть весь алгоритм в виде одного выражения. То есть (убрав квалификацию пространства имен, чтобы получить все в одной строке):

duration<double, days::period> days = sd - sys_days{ymd_ref} + now - sd;

И это явно алгебраически упрощается до:

duration<double, days::period> days = now - sys_days{ymd_ref};

В итоге:

using namespace std::chrono;
using namespace date;

// Get actual time.
auto now = system_clock::now();

// Get the start of the year and subract it from now.
using ddays = duration<double, days::period>;
ddays fd = now - sys_days{year_month_day{floor<days>(now)}.year()/1/1};

// Get fractional day number.
std::cout << "Fractional day of the year: " << fd.count() << '\n';

В этом случае, разрешив <chrono> выполнить преобразования для нас, можно было достаточно упростить код, так что сам алгоритм мог быть алгебраически упрощен, что привело к получению более чистого и более эффективного кода, который доказуемо эквивалентен исходному алгоритму в вопрос ОП.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...