С++ 11 std::chrono вычесть сейчас и мин.

Я чувствую, что схожу с ума с этим, но это просто не имеет для меня смысла. На мой взгляд, если я вычту минимальный момент времени из любого момента времени, возвращаемого вызовом now(), я всегда должен получить положительную продолжительность, но этого не происходит.

#include <chrono>
#include <iostream>

typedef std::chrono::steady_clock myclock;

int main(int argc, char **argv) {
        myclock::time_point min = myclock::time_point::min();
        myclock::time_point now = myclock::now();
        auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(now - min).count();
        std::cout << millis << std::endl;
}

Почему это печатает отрицательное целое число, а не положительное целое число? (clang 3.3 или g++ 4.8.1)


person LorenVS    schedule 15.02.2014    source источник
comment
Не обязательно связано, вы хотите вывести разницу во времени в секундах или миллисекундах?   -  person Martin J.    schedule 15.02.2014
comment
Спасибо, что указали на это, я пытался проверить, не переполнился ли я, потому что миллисекундный результат был слишком большим. я поменяю обратно   -  person LorenVS    schedule 15.02.2014
comment
Вы переполняете счетчик и получаете отрицательный результат   -  person Marco A.    schedule 15.02.2014
comment
Я получаю такое же (неправильное) поведение с MSVC++ 2012 (он же Версия 11.0)   -  person T.E.D.    schedule 17.01.2015


Ответы (3)


Как было указано, это результат переполнения. Помните, что минимальное значение, которое может представлять знаковый тип, примерно равно величине наибольшего. Если now положителен, то разница между now и min явно будет иметь большую величину, чем min, что означает, что она имеет большую величину, чем может представлять наибольшее значение типа.

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

Другой вариант — выбрать часы, для которых известно, что эпоха уже в прошлом, и просто сказать:

Clock::now().time_since_epoch();
person bames53    schedule 15.02.2014
comment
Проблема с этим решением заключается в том, что дайте мне минимальное время традиционно было сокращением программиста для дайте мне время, которое гарантированно истекает, независимо от того, что кто-то выбирает в качестве задержки истечения. Я не понимаю, как это сделать с другим относительным значением времени. - person T.E.D.; 17.01.2015
comment
При этом я все еще поддерживаю это, потому что это, минус около 1000 часов, было лучшим решением, которое я вижу. Но я не понимаю, как такое поведение не считается ошибкой. - person T.E.D.; 17.01.2015
comment
@ТЕД. для этого я бы сказал, что clock::min() - это правильно: clock::min() < clock::now() всегда должно оцениваться как true на практике. Нет необходимости выполнять потенциально переполняющую арифметику, если вам просто нужно время меньше любого текущего времени. Также помните, что даже несмотря на то, что «традиционные» API-интерфейсы времени не различают в системе типов точки времени и продолжительность, по-прежнему важно не использовать продолжительность там, где находится точка времени, и наоборот. - person bames53; 17.01.2015

Вы переполняете счетчик, который на моей машине равен signed long long

#include <chrono>
#include <iostream>
#include <limits.h>
using namespace std;

typedef std::chrono::steady_clock myclock;

int main(int argc, char **argv) {
    myclock::time_point min = myclock::time_point::min();
    long long minl = reinterpret_cast<long long&>(min);
    cout << reinterpret_cast<long long&>(min) << endl;

    auto now = myclock::now();
    long long nowl = reinterpret_cast<long long&>(now);
    cout << reinterpret_cast<long long&>(now) << endl;

    cout << (nowl-minl) << endl;

    cout << "max of signed long long:" << LLONG_MAX << endl;

    auto millis = std::chrono::duration_cast<std::chrono::seconds>(now - min).count();
    //std::cout << millis << std::endl;
}

Выход:

-9223372036854775808
13924525439448122
-9209447511415327686
max of signed long long:9223372036854775807
person Marco A.    schedule 15.02.2014

Вот что, по моему мнению, происходит (из того, что я наблюдаю в своем отладчике в Apple-llvm 5.0):

myclock::time_point::min() возвращает самый ранний момент времени, который обычно внутренне представлен целочисленным типом, скажем, long long int. Минимум такого типа обычно равен numeric_limits<long long int>::min, что равно -2^63. Это значение особенное тем, что если вы отмените его, вы получите то же значение через целочисленное переполнение (поскольку максимальное длинное длинное целое равно 2 ^ 63 -1):

-(-2^63) == 2^63 == (2^63 - 1) + 1 == -2^63 (по переполнению)

Та же логика применима и к вычитанию.
Все это говорит о том, что целочисленное переполнение делает (now - min) на самом деле эквивалентным (now + min), что обязательно отрицательно.

person Martin J.    schedule 15.02.2014
comment
Примечание: фактическое целочисленное переполнение со знаком формально является неопределенным поведением, поэтому ваше объяснение прагматично, но любой компилятор (и оптимизатор) может нарушить этот код. - person Matthieu M.; 15.02.2014
comment
Да, я больше сообщал о том, что я наблюдал на своей установке, которая показала то же поведение, что и то, что наблюдал LorenVS. - person Martin J.; 15.02.2014