Функция<std::numeric_limits<T>::max()
>возвращает наибольшее конечное значение, которое может быть представлено типом T. Если же такой величины нет, то<T()
>возвращается.
Для встроенных типов обычно имеется соответствующее значение MACRO TYPE_MAX, где TYPE - CHAR, INT, FLOAT и т.д.
Другие типы, в том числе те, которые предоставляются типдефом, например<INT64_T_MAX
>для<int64_t
>, могут обеспечивать макроопределение.
Для удовлетворения ситуаций, когда нет специализации<numeric_limits
>(например, из-за того, что точность типа изменяется во время выполнения), упакованные версии этой (и других функций) предоставляются с использованием
#include <boost/math/tools/precision.hpp>
T = boost::math::tools::max_value<T>();
Конечно, они просто используют<std::numeric_limits<T>::max()
>, если доступны, но в противном случае «делай что-то разумное».
C++11:<std::numeric_limits<T>::lowest()
>является
- Для интегральных типов такая же функция<
min()
>.
- Для типов с плавающей запятой, как правило, отрицательный<
max()
>(но зависящий от реализации).
-(std::numeric_limits<double>::max)() == std::numeric_limits<double>::lowest();
Функция<std::numeric_limits<T>::min()
>возвращает минимальное конечное значение, которое может быть представлено типом T.
Для встроенных типов обычно имеется соответствующее значение MACRO TYPE_MIN, где TYPE - CHAR, INT, FLOAT и т.д.
Другие типы, в том числе те, которые предоставляются типдефом, например<INT64_T_MIN
>для<int64_t
>, могут обеспечивать макроопределение.
Для типов с плавающей точкой он более полно определяется какминимальное положительное нормализованное значение.
<std::numeric_limits<T>::denorm_min()
>для наименьшей денормализованной величины, предусмотренной
std::numeric_limits<T>::has_denorm == std::denorm_present
Для удовлетворения ситуаций, когда нет специализации<numeric_limits
>(например, из-за того, что точность типа изменяется во время выполнения), упакованные версии этой (и других функций) предоставляются с использованием
#include <boost/math/tools/precision.hpp>
T = boost::math::tools::min_value<T>();
Конечно, они просто используют<std::numeric_limits<T>::min()
>.
Функция<std::numeric_limits<T>::denorm_min()
>возвращает наименьшееденормализованное значение, при условии
std::numeric_limits<T>::has_denorm == std::denorm_present
std::cout.precision(std::numeric_limits<double>::max_digits10);
if (std::numeric_limits<double>::has_denorm == std::denorm_present)
{
double d = std::numeric_limits<double>::denorm_min();
std::cout << d << std::endl;
int exponent;
double significand = frexp(d, &exponent);
std::cout << "exponent = " << std::hex << exponent << std::endl;
std::cout << "significand = " << std::hex << significand << std::endl;
}
else
{
std::cout << "No denormalization. " << std::endl;
}
Экспонент эффективно уменьшается с -308 до -324 (хотя он остается закодированным как ноль и ведущие нули появляются в значении, тем самым теряя точность до тех пор, пока значение не достигнет нуля).
Функция<std::numeric_limits<T>::round_error()
>возвращает максимальную ошибку (в единицахULP), которая может быть вызвана любой базовой арифметической операцией.
round_style == std::round_indeterminate;
Стиль округления неопределим во время компиляции.
Для типов с плавающей точкой, когда округление близко, только половина бита теряется при округлении и<round_error
==0.5
>. В противоположность этому, когда округление приближается к нулю или плюс/минус бесконечности, мы можем освободить до одного бита от округления и<round_error
==1
>.
Для целых типов округление всегда равно нулю, поэтому в худшем случае можно округлить почти один бит, так<round_error
==1
>.
<round_error()
>может использоваться с<std::numeric_limits<T>::epsilon()
>для оценки максимальной потенциальной ошибки, вызванной округлением. Для типов с плавающей запятой<round_error()=1/2
>половина эпсилона является максимальной потенциальной ошибкой.
double round_err = std::numeric_limits<double>::epsilon()
* std::numeric_limits<double>::round_error();
std::cout << round_err << std::endl;
Конечно, есть много случаев, когда происходит гораздо большая потеря точности, например, вызванная. Потеря значимости или ошибка отменыили очень много итераций.
Функция<std::numeric_limits<T>::epsilon()
>имеет значение только для неинтегральных типов.
Он возвращает разницу между<1.0
>и следующим значением, представляемым типом T с плавающей точкой. Таким образом, это одно наименьшее изменение бита в этом значении с плавающей точкой.
Для<double
><float_64t
><2.2204460492503131e-016
>он показывает все, возможно, значительные 17 десятичных знаков.
std::cout.precision(std::numeric_limits<double>::max_digits10);
double d = 1.;
double eps = std::numeric_limits<double>::epsilon();
double dpeps = d+eps;
std::cout << std::showpoint
<< d << "\n"
<< dpeps << std::endl;
std::cout << dpeps - d
<< std::endl;
Мы можем явно увеличить на один бит, используя функцию<boost::math::float_next()
>, и результат такой же, как добавление<epsilon
>.
double one = 1.;
double nad = boost::math::float_next(one);
std::cout << nad << "\n"
<< nad - one
<< std::endl;
Добавление любого меньшего значения, такого как половина<epsilon
>, не повлияет на это значение.
std::cout.precision(std::numeric_limits<double>::max_digits10);
double d = 1.;
double eps = std::numeric_limits<double>::epsilon();
double dpeps = d + eps/2;
std::cout << std::showpoint
<< dpeps << "\n"
<< eps/2 << std::endl;
std::cout << dpeps - d
<< std::endl;
Таким образом, эта ошибка отмены оставляет значения равными, несмотря на добавление половины<epsilon
>.
Для достижения большей переносимости по платформе и типу с плавающей запятой Boost. Математика и рост. Multiprecion предоставляет пакет функций, которые «делают что-то разумное», если стандарт<numeric_limits
>недоступен. Используйте их<#include
<boost/math/tools/precision.hpp>
>.
Допуск может быть определен с использованием этой версии эпсилона таким образом:
RealType tolerance = boost::math::tools::epsilon<RealType>() * 2;
<epsilon
>очень полезно вычислить допуск при сравнении значений с плавающей точкой, что гораздо более сложная задача, чем принято считать.
Для получения дополнительной информации вы, вероятно, хотите (но все еще нуждаетесь) см.Что должен знать каждый компьютерный ученый об арифметике с плавающей точкой
Наивный тест сравнения абсолютной разницы между двумя значениями и допуском не дает полезных результатов, если значения слишком велики или слишком малы.
Так поднимись. Тест использует алгоритм, впервые разработанный Кнутом для надежной проверки того, достаточно ли близки значения с плавающей точкой.
Увидимся, Дональд. Э. Кнут. Искусство компьютерного программирования (vol II). Copyright 1998 Addison-Wesley Longman, Inc., 0-201-89684-2. Addison-Wesley Professional; 3-е издание.
Смотрите также:
Альберто Сквассия, Сравнение поплавков
Альберто Сквассия, Сравнение поплавкового кода
Сравнение с плавающей точкой.
Например, если мы хотим получить допуск, который мог бы соответствовать примерно 9 арифметическим операциям, скажем sqrt(9) = 3, мы могли бы определить:
T tolerance = 3 * std::numeric_limits<T>::epsilon();
Это очень широко используется в Boost. Тестирование с помощью Boost. Тестовый макрос<BOOST_CHECK_CLOSE_FRACTION
>
T expected = 1.0;
T calculated = 1.0 + std::numeric_limits<T>::epsilon();
BOOST_CHECK_CLOSE_FRACTION(expected, calculated, tolerance);
Используется таким образом:
BOOST_CHECK_CLOSE_FRACTION(expected, calculated, tolerance);
(Существует также версия, использующая допуск в процентах, а не в долях).
using boost::multiprecision::number;
using boost::multiprecision::cpp_dec_float;
using boost::multiprecision::et_off;
typedef number<cpp_dec_float<50>, et_off > cpp_dec_float_50;
![[Note]](/img/note.png) |
Note |
Этот буст. Тест еще не позволяет сравнивать с шаблонами экспрессии с плавающей точкой, поэтому параметр шаблона экспрессии по умолчанию был заменен<et_off >. |
cpp_dec_float_50 tolerance = 3 * std::numeric_limits<cpp_dec_float_50>::epsilon();
cpp_dec_float_50 expected = boost::math::constants::two_pi<cpp_dec_float_50>();
cpp_dec_float_50 calculated = 2 * boost::math::constants::pi<cpp_dec_float_50>();
BOOST_CHECK_CLOSE_FRACTION(expected, calculated, tolerance);
Только для типов с плавающей запятой, для которых<std::numeric_limits<T>::has_infinity
==true
>, функция<std::numeric_limits<T>::infinity()
>обеспечивает определяемое реализацией представление для ∞.
«Представление» — это особый битовый шаблон, зарезервированный для бесконечности. Для системы IEEE754 (для которой<std::numeric_limits<T>::is_iec559
==true
>)положительная и отрицательная бесконечностьназначаются битовые узоры для всех заданных типов с плавающей запятой.
Смущает то, что строка, возникающая в результате вывода этого представления, также определяется реализацией. И строка, которая может быть введена для создания представления, также определяется реализацией.
Например, выход<1.#INF
>на системах Microsoft, но<inf
>на большинстве *nix платформ.
Эта реализация-определённость затрудняет использование бесконечности (и NaNs), но усиливается. Математика и рост. Многоточность работы позволяет обеспечить разумное представление длявсехтипов с плавающей запятой, а не только встроенных типов, которые с использованием подходящих граней для определения входных и выходных строк позволяют использовать эти полезные функции переносимо и включая Boost.Serialization.
Для типов с плавающей запятой, для которых<std::numeric_limits<T>::has_quiet_NaN
==true
>, функция<std::numeric_limits<T>::quiet_NaN()
>обеспечивает заданное реализацией представление для NaN.
NaN— значения, указывающие на то, что результат назначения или вычисления бессмыслен. Типичный пример<0/0
>, но есть много других.
Для представления отсутствующих значений могут также использоваться NaN: например, они могут, по условности, игнорироваться при расчетах статистики как средства.
Многие проблемы с представлением дляNot-A-Numberзатрудняют портативное использование, подобно тем, которые имеют бесконечность.
NaN может использоваться с бинарными типами многоточности, такими как<cpp_bin_float_quad
>:
using boost::multiprecision::cpp_bin_float_quad;
if (std::numeric_limits<cpp_bin_float_quad>::has_quiet_NaN == true)
{
cpp_bin_float_quad tolerance = 3 * std::numeric_limits<cpp_bin_float_quad>::epsilon();
cpp_bin_float_quad NaN = std::numeric_limits<cpp_bin_float_quad>::quiet_NaN();
std::cout << "cpp_bin_float_quad NaN is " << NaN << std::endl;
cpp_bin_float_quad expected = NaN;
cpp_bin_float_quad calculated = 2 * NaN;
bool b = expected == calculated;
std::cout << b << std::endl;
BOOST_CHECK_NE(expected, expected);
BOOST_CHECK_NE(expected, calculated);
}
else
{
std::cout << "Type " << typeid(cpp_bin_float_quad).name() << " does not have NaNs!" << std::endl;
}
Но с помощью Boost. Математика и подходящие грани могут позволить портативное использование как NaN, так и положительной и отрицательной бесконечности.
См.boost:/libs/math/example/nonfinite_facet_sstream.cpp
#include <boost/math/special_functions/nonfinite_num_facets.hpp>
Тогда мы также можем использовать многоточный тип cpp_bin_float_quad:
using boost::multiprecision::cpp_bin_float_quad;
typedef cpp_bin_float_quad T;
using boost::math::nonfinite_num_put;
using boost::math::nonfinite_num_get;
{
std::locale old_locale;
std::locale tmp_locale(old_locale, new nonfinite_num_put<char>);
std::locale new_locale(tmp_locale, new nonfinite_num_get<char>);
std::stringstream ss;
ss.imbue(new_locale);
T inf = std::numeric_limits<T>::infinity();
ss << inf;
assert(ss.str() == "inf");
T r;
ss >> r;
assert(inf == r);
std::cout << "infinity output was " << ss.str() << std::endl;
std::cout << "infinity input was " << r << std::endl;
}
infinity output was inf
infinity input was inf
Точно так же мы можем сделать то же самое с NaN (кроме того, что мы не можем использовать<assert
>).
{
std::locale old_locale;
std::locale tmp_locale(old_locale, new nonfinite_num_put<char>);
std::locale new_locale(tmp_locale, new nonfinite_num_get<char>);
std::stringstream ss;
ss.imbue(new_locale);
T n;
T NaN = std::numeric_limits<T>::quiet_NaN();
ss << NaN;
assert(ss.str() == "nan");
std::cout << "NaN output was " << ss.str() << std::endl;
ss >> n;
std::cout << "NaN input was " << n << std::endl;
}
NaN вход был NaN вход был Nan
Только для типов с плавающей запятой, для которых<std::numeric_limits<T>::has_signaling_NaN
==true
>, функция<std::numeric_limits<T>::signaling_NaN()
>обеспечивает заданное реализацией представление для NaN, которое вызывает аппаратную ловушку. Следует отметить, однако, что по меньшей мере одна реализация этой функции вызывает аппаратную ловушку, которая вызывается просто вызовом<std::numeric_limits<T>::signaling_NaN()
>, а не только использованием возвращаемого значения.