Почему переполнение (-2147483648) происходит в коде?

Я вижу довольно странную проблему. Каким-то образом, с моим кодом ниже, я вижу, как выводится отрицательное число, как показано ниже в моем holder переменная. Я не уверен, почему это происходит.

-2147483648 days -2147483648 hours -2147483648 minutes ago

Вот мое значение метки времени (current_unix_timestamp) 1437943320 который передается моему методу ниже, а затем потом holder значение приходит, как показано выше, как отрицательное.

char holder[100];
get_timestamp_value(current_unix_timestamp, holder);

inline void get_timestamp_value(long sec_since_epoch_time, char* holder) {
    uint64_t timestamp = current_timestamp();
    double delta = timestamp/1000000 - sec_since_epoch_time;
    int days = floor(delta/60/60/24);
    int hours = floor((delta - days * 60 * 60 * 24)/60/60);
    int minutes = floor((delta - days * 60 * 60 * 24 - hours * 60 * 60)/60);
    holder[0] = 0;
    if (days) sprintf(holder, "%d days ", days);
    if (hours) sprintf(holder, "%s%d hours ", holder, hours);
    sprintf(holder, "%s%d minutes ago", holder, minutes);
    std::cout<< "Timestamp: " << timestamp << ", sec_since_epoch_time: " << sec_since_epoch_time << ", Delta:" << delta << ", Days: " << days << ", hours: " << hours << ", mins: " << mins << std::endl;
}

// get current system time in microseconds since epoch
inline uint64_t current_timestamp()
{
    std::chrono::time_point<std::chrono::steady_clock> ts = std::chrono::steady_clock::now();
    return std::chrono::duration_cast<std::chrono::microseconds>(ts.time_since_epoch()).count();
}

Теперь это то, что распечатано из выше cout журналы:

Timestamp: 433430278724, sec_since_epoch_time: 1437943320, Delta:1.84467e+19, Days: -2147483648, hours: -2147483648, mins: -2147483648
Timestamp: 433679536303, sec_since_epoch_time: 1437943380, Delta:1.84467e+19, Days: -2147483648, hours: -2147483648, mins: -2147483648
Timestamp: 433929683258, sec_since_epoch_time: 1437943440, Delta:1.84467e+19, Days: -2147483648, hours: -2147483648, mins: -2147483648
Timestamp: 434179628271, sec_since_epoch_time: 1437943500, Delta:1.84467e+19, Days: -2147483648, hours: -2147483648, mins: -2147483648

Что-то не так происходит в приведенном выше коде, который вызывает эту проблему? Любые предложения будут очень полезны.

1 ответ

Вам следует избегать арифметических операций со смесью целых типов со знаком и без знака. Результаты часто бывают удивительными.

Очевидно, что timestamp это не то значение, которое вы ожидаете, так как timestamp/1000000 433430, что значительно меньше sec_since_epoch_time, Следовательно, вы можете ожидать timestamp/1000000 - sec_since_epoch_time быть отрицательным числом, но (как ни удивительно, как указано выше), это будет большое положительное число, потому что подписанный длинный sec_since_epoch_time превращается в unsigned long до вычитания, следуя правилам для обычных арифметических преобразований. Вычитание затем выполняется с использованием арифметики без знака, так что результатом является положительное число, немного меньшее 264, как видно из значения delta,

Деления этого большого числа на 86400 недостаточно, чтобы привести его в диапазон int, так что назначение

int days = floor(delta/60/60/24);

будет переполнен с неопределенными последствиями (в этом случае настройка days до -231.)

Мне кажется немного странным спрашивать длительность в микросекундах, а затем делить ее на миллион. Почему бы просто не спросить продолжительность в секундах?

Но основная проблема заключается в том, что вы сравниваете значение, возвращаемое current_timestamp, который является числом микросекунд с эпохи std::chrono::steady_clockс аргументом sec_since_epoch_time, Это выглядит как sec_since_epoch_time количество секунд с начала эпохи Unix (1 января 1970 г.). Тем не менее, нет никаких гарантий, что эпоха для std::chrono часы имеют это значение. (Видимо, в Linux эпоха для std::chrono:system_clock это системная эпоха, но повторюсь, гарантии нет.)

Вы не можете сравнивать две "секунды с начала эпохи", если они не являются секундами с той же эпохи. Это означает, что ваш код может работать только в том случае, если часы, которые изначально использовались для получения значения sec_since_epoch_time такие же часы, как вы используете в current_timestamp,

В дополнение к обеспечению того, что timestamp на самом деле имеет ожидаемое значение, вы должны изменить timestamp для int64_t (или приведите его к int64_t для целей расчета delta).

Другие вопросы по тегам