Как обойти противоречивое определение numeric_limits‹T›::min()?

Черты numeric_limits должны быть общим способом получения различной информации о типах, чтобы иметь возможность делать такие вещи, как

template<typename T>
T min(const std::vector<T>& vect)
{
    T val = std::numeric_limits<T>::min();

    for(int i=0 ; i<vect.size() ; i++)
        val = max(T, vect[i]);

    return val;
}

Проблема в том, что (по крайней мере, при использовании MS Visual Studio 2008) numeric_limits‹int›::min() возвращает наименьшее отрицательное число, а numeric_limits‹double›::min() возвращает наименьшее положительное число. !

Кто-нибудь знает, что стоит за этим дизайном? Есть ли лучший (рекомендуемый?) способ использования numeric_limits? В моей конкретной функции выше я, конечно, мог бы инициализировать T в vect[0], но это не тот ответ, который я ищу.

См. также обсуждение (для операций с плавающей запятой) здесь


person Community    schedule 29.04.2009    source источник
comment
В вашем примере две ошибки. 1. Функция должна называться max(), так как она вычисляет максимальный элемент. 2. строка val = max(T, vect[i]) должна быть val = max(val, vect[i]).   -  person TonJ    schedule 05.05.2009


Ответы (7)


Вы можете использовать библиотеки Boost. Библиотека Numeric Conversions предоставляет класс, называемый границами, который можно использовать последовательно.

См. документацию здесь.

person Cătălin Pitiș    schedule 29.04.2009

Это старая тема, но есть обновленный ответ:

C++11 добавил функцию lowest() к std::numeric_limits (см. здесь)

Итак, теперь вы можете вызвать std::numeric_limits<double>::lowest(), чтобы получить наименьшее представимое отрицательное значение.

person Alex Goldberg    schedule 20.02.2013

Поведение min() не такое уж и странное, она возвращает FLT_MIN, DBL_MIN или INT_MIN (или их соответствующие значения), в зависимости от типа, на котором вы специализируетесь. Таким образом, ваш вопрос должен заключаться в том, почему FLT_MIN и DBL_MIN определяются иначе, чем INT_MIN.

К сожалению, я не знаю ответа на последний вопрос.

Я подозреваю, что это было определено таким образом для практических целей. Для целых чисел вы обычно имеете дело с переполнением/опустошением, когда интерес представляют минимальное и максимальное значения.

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

person Community    schedule 29.04.2009

Обходной путь был бы

double val = -std::numeric_limits<double>::max();

Конечно, это не объясняет странного поведения numerics_limits::min(), которое может быть результатом того факта, что существуют разные минимальные/максимальные границы для целых чисел (min = -2^n, max = 2^n- 1), но не для двойников.

person schnaader    schedule 29.04.2009
comment
Является ли std::numeric_limits‹int›::max() == -std::numeric_limits‹int›::min() ? - person Martin York; 29.04.2009
comment
Я так не думаю - как я уже писал, я думаю, что min = -2 ^ 31, max = 2 ^ 31-1, например, для 32-битных типов данных. Это представляет наибольшее отрицательное/положительное значение int - gcc подтверждает это: min: -2147483648, max: 2147483647 - person schnaader; 29.04.2009
comment
Нет, мой код для double, а не для int. Для двойного типа поведение другое (min дает наименьшее положительное двойное значение) - в этом суть вопроса OP. Таким образом, обходным путем для получения наибольшего отрицательного значения (ожидаемое минимальное поведение) будет этот код. Конечно, это не будет работать для всех типов или шаблонов, только для особого случая двойного типа. - person schnaader; 29.04.2009
comment
Границы повышения (1.38) говорят о lowest: возвращает минимальное конечное значение, эквивалентное numeric_limits‹T›::min(), когда T является целым типом, и -numeric_limits‹T›::max(), когда T является плавающим. тип точки. boost.org/doc/ libs/1_38_0/libs/numeric/conversion/doc/html/ - person Alessandro Jacopson; 12.09.2011

Я не уверен в обосновании, но это ожидаемое поведение. Ну, в том смысле, как это описывает Йосуттис (и, предположительно, стандарт)!

min(): "Минимальное конечное значение (минимальное нормализованное значение для типов с плавающей запятой с денормализацией)".

Насколько я могу судить, если тип не целочисленный (numeric_limits<>::is_integer) и имеет денормализацию (numeric_limits<>::has_denorm), min() вернет наименьшее представимое значение для этого типа. В противном случае будет возвращено наименьшее значение, которое может быть отрицательным.

Для более согласованного интерфейса ознакомьтесь с Повысьте числовую/конверсионную библиотеку. В частности, класс bounds признаков< /а>. Вот фрагмент:

cout << "lowest float:" << boost::numeric::bounds<float>::lowest();
cout << "lowest int:  " << boost::numeric::bounds<int>::lowest();

Вы также можете найти библиотеку boost::integer. полезный. Он привносит часть целочисленной поддержки C99 (например, int_least16_t) в C++ и может помочь выбрать тип наилучшего размера для ваших конкретных нужд. Пример:

boost::uint_t<20>::fast fastest20bits; // fastest unsigned integer that 
                                       // can hold at least 20 bits.
boost::int_max_value_t<100000>::least  // smallest integer that can store
                                       // the value 100000.

Я часто обнаруживаю, что когда мне нужен один из boost::numeric/conversion или boost::integer, мне нужны оба.

person MattyT    schedule 29.04.2009

numeric_limits<int>::min возвращает наименьшее отрицательное число, все типы чисел с плавающей запятой возвращают наименьшее положительное число, когда я пробовал это с Sun CC и g++.

Я предполагаю, что это потому, что «наименьший» и «минимальный» означают разные вещи с числами с плавающей запятой. Хотя это немного странно.

И Sun CC, и g++ дают одинаковый результат:

короткий: мин: -32768 макс: 32767

интервал: мин: -2147483648 макс: 2147483647

беззнаковое целое: мин: 0 макс: 4294967295

длинный: мин: -2147483648 макс: 2147483647

поплавок: мин.: 1.17549e-38 макс.: 3.40282e+38

двойной: мин.: 2.22507e-308 макс.: 1.79769e+308

длинный двойной: мин.: 3.3621e-4932 макс.: 1.18973e+4932

беззнаковое короткое: мин: 0 макс: 65535

беззнаковое целое: мин: 0 макс: 4294967295

беззнаковый длинный: мин: 0 макс: 429496729

template<typename T>
void showMinMax()
{
    cout << "min: " << numeric_limits<T>::min() << endl;
    cout << "max: " << numeric_limits<T>::max() << endl;
    cout << endl;
}

int main()
{
cout << "short:";
showMinMax<short>()
...etc...etc..
person Chris Huang-Leaver    schedule 29.04.2009
comment
Странно, numeric_limits‹double›::min возвращает здесь очень маленькое положительное число (g++ 3.4.5). Возможно, это зависит от версии/ОС/реализации - person schnaader; 30.04.2009

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

Вместо этого лучше использовать std::min_element:

int main()
{
    std::vector<int> v;
    std::generate_n(std::back_inserter(v), 1000, std::rand);

    std::vector<int>::iterator it  = std::min_element(v.begin(), v.end());
    if (it == v.end())
    {
        std::cout << "There is no smallest element" << std::endl;
    }
    else
    {
        std::cout << "The smallest element is " << *it << std::endl;
    }
}
person dalle    schedule 29.04.2009