Наиболее эффективный способ определения значения между двумя другими значениями, включительно - PullRequest
0 голосов
/ 09 января 2019

Аналогично Самый быстрый способ определить, находится ли целое число между двумя целыми числами (включительно) с известными наборами значений , я хочу выяснить, есть ли какое-либо значение (скорее всего, число с плавающей запятой двойной точности) находится между двумя другими значениями (того же типа). Предостережение в том, что я не знаю, какое значение больше другого, и я пытаюсь определить, следует ли мне / как мне следует избегать использования std :: max / min. Вот код, с которым я уже пытался это проверить:

inline bool inRangeMult(double p, double v1, double v2) {
    return (p - v1) * (p - v2) <= 0;
}

inline bool inRangeMinMax(double p, double v1, double v2) {
    return p <= std::max(v1, v2) && p >= std::min(v1, v2);
}

inline bool inRangeComp(double p, double v1, double v2) {
    return p < v1 != p < v2;
}

int main()
{
    double a = 1e4;

    std::clock_t start;
    double duration;
    bool res = false;

    start = std::clock();
    for (size_t i = 0; i < 2e4; ++i) {
        for (size_t j = 0; j < 2e4; ++j) {
            res = inRangeMult(a, i, j) ? res : !res;
        }
    }
    duration = std::clock() - start;
    std::cout << "InRangeMult: " << duration << std::endl;

    start = std::clock();
    for (size_t i = 0; i < 2e4; ++i) {
        for (size_t j = 0; j < 2e4; ++j) {
            res = inRangeMinMax(a, i, j) ? res : !res;
        }
    }
    duration = std::clock() - start;
    std::cout << "InRangeMinMax: " << duration << std::endl;

    start = std::clock();
    for (size_t i = 0; i < 2e4; ++i) {
        for (size_t j = 0; j < 2e4; ++j) {
            res = inRangeComp(a, i, j) ? res : !res;
        }
    }
    duration = std::clock() - start;
    std::cout << "InRangeComp: " << duration << std::endl;

    std::cout << "Tricking the compiler by printing inane res: " << res << std::endl;
}

На большинстве прогонов я нахожу, что использование std :: min / max по-прежнему самое быстрое (последние прогоны печатают 346, 310 и 324 соответственно), но я не уверен на 100%, что это лучшая тестовая установка, или что я исчерпал все разумные реализации.

Буду признателен за любой вклад с лучшей настройкой профилирования и / или лучшей реализацией.

РЕДАКТИРОВАТЬ: обновлен код, чтобы сделать его менее склонным к оптимизации компилятора.

2-е РЕДАКТИРОВАНИЕ: измененное значение и количество итераций. Результаты за один прогон:

  • inRangeMult: 1337
  • inRangeMinMaz: 1127
  • inRangeComp: 729

1 Ответ

0 голосов
/ 10 января 2019

Первый тест:

(p - v1) * (p - v2) <= 0

Может привести к переполнению или понижению из-за арифметических операций.

Последний:

p < v1 != p < v2

Не дает те же результаты, что и другие, которые включают в себя в отношении границ v1 и v2. Это, по общему признанию, небольшое различие, учитывая дальность и точность типа double, но оно может быть значительным.

Другим вариантом является явное расширение логики второй функции:

p <= std::max(v1, v2) && p >= std::min(v1, v2)     // v1 and v2 are compared twice

На что-то вроде этого:

bool inRangeComp(double p, double v1, double v2) {
    return v1 <= v2                                // <- v1 and v2 are compared only once
        ? v1 <= p && p <= v2 
        : v2 <= p && p <= v1;
}

По крайней мере, один компилятор (gcc 8.2), ЗДЕСЬ (спасибо jarod42 за связанный фрагмент), похоже, предпочитает эту версию альтернативам.

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