std::numeric_limits::is_exact ... что такое пригодное для использования определение?

Как я понимаю, определение MSDN numeric_limits::is_exact почти всегда ложно

[все] вычисления, выполненные для [этого] типа, не содержат ошибок округления.

И определение IBM почти всегда верно: (Или круговое определение, в зависимости от того, как вы его читаете)

тип, который имеет точные представления для всех своих значений

Я уверен в том, что могу хранить 2 в обоих double и long и они оба будут представлены точно.

Я мог бы тогда разделить их на 10 и ни один не будет держать математический результат точно.

Учитывая любой числовой тип данных T , как правильно определить std::numeric_limits<T>::is_exact ?

Изменить: я опубликовал то, что я думаю, является точным ответом на этот вопрос из деталей, представленных во многих ответах. Этот ответ не претендует на награду.

6 ответов

Решение

Определение в стандарте (см. Ответ NPE) не очень точное, не так ли? Вместо этого это является круглым и расплывчатым.

Учитывая, что стандарт IEC с плавающей запятой имеет понятие "неточных" чисел (и неточное исключение, когда вычисление дает неточное число), я подозреваю, что это происхождение имени is_exact, Обратите внимание, что из стандартных типов, is_exact ложно только для float, double, а также long double,

Цель состоит в том, чтобы указать, точно ли тип представляет все числа базового математического типа. Для целочисленных типов базовый математический тип - это некоторое конечное подмножество целых чисел. Так как каждый целочисленный тип точно представляет каждого члена подмножества целых чисел, на которые нацелен этот тип, is_exact верно для всех целочисленных типов. Для типов с плавающей запятой базовый математический тип представляет собой некоторое подмножество конечных чисел действительных чисел. (Примером подмножества конечного диапазона является "все действительные числа от 0 до 1".) Нет способа точно представить даже подмножество конечного диапазона действительных чисел; почти все неисчислимы. Формат IEC/IEEE усугубляет ситуацию. С этим форматом компьютеры не могут даже точно представить подмножество конечного диапазона рациональных чисел (не говоря уже о подмножестве конечного диапазона вычислимых чисел).

Я подозреваю, что происхождение термина is_exact это давняя концепция "неточных" чисел в различных моделях представления с плавающей запятой. Возможно, лучшее имя было бы is_complete,

добавление
Числовые типы, определяемые языком, не являются первичными и конечными представлениями "чисел". Представление с фиксированной точкой - это, по сути, целые числа, поэтому они тоже будут точными (без дыр в представлении). Представление рациональных чисел в виде пары стандартных целочисленных типов (например, int/int) не будет точным, но класс, который представлял рациональные как Bignum пара будет, по крайней мере теоретически, "точной".

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

Второе дополнение
Место для начала со стандартом. И C++03, и C++11 определяют is_exact как существо

True, если тип использует точное представление.

Это и расплывчато, и кругло. Это бессмысленно. Не совсем так бессмысленно, что целочисленные типы (char, short, int, longи т. д.) являются "точными" по указу:

Все целые типы являются точными,...

А как насчет других арифметических типов? Первое, что нужно отметить, это то, что единственными другими арифметическими типами являются типы с плавающей запятой. float, double, а также long double (3.9.1/8):

Есть три типа с плавающей точкой: float, double, а также long double,... Представление значений типов с плавающей запятой определяется реализацией. Интегральные и плавающие типы вместе называются арифметическими типами.

Значение типов с плавающей запятой в C++ заметно мутно. Сравните с Фортраном:

Реальные данные - это приближение процессора к значению действительного числа.

Сравните с ИСО / МЭК 10967-1 "Арифметика, не зависящая от языка" (на которую ссылаются стандарты С ++ в сносках, но не в качестве нормативной ссылки):

Тип F с плавающей точкой должен быть конечным подмножеством ℝ.

C++ с другой стороны, это спорный вопрос относительно того, что типы с плавающей точкой должны представлять. Насколько я могу сказать, реализация может сойти с float синоним для int, double синоним для long, а также long double синоним для long long,

Еще раз из стандартов на is_exact:

... но не все точные типы являются целыми. Например, рациональные и фиксированные представления являются точными, но не целочисленными.

Это очевидно не относится к разработанным пользователем расширениям по той простой причине, что пользователям не разрешается определять std::whatever<MyType>, Сделайте это, и вы вызываете неопределенное поведение. Этот последний пункт может относиться только к реализациям, которые

  • определять float, double, а также long double каким-то своеобразным образом, или
  • Предоставьте некоторый нестандартный рациональный тип или тип с фиксированной точкой в ​​качестве арифметического типа и решите предоставить std::numeric_limits<non_standard_type> для этих нестандартных расширений.

Я полагаю, что is_exact имеет значение true, если все литералы этого типа имеют точное значение. Таким образом, is_exact имеет значение false для плавающих типов, потому что значение литерала 0.1 точно не равно 0.1.

Согласно комментарию Кристиана Рау, мы можем вместо этого определить is_exact как истинное, когда результаты четырех арифметических операций между любыми двумя значениями типа либо выходят за пределы диапазона, либо могут быть представлены точно, используя определения операций для этого типа (т.е., усеченное целочисленное деление, беззнаковый перенос). С помощью этого определения вы можете скрыть, что операции с плавающей точкой определены для получения ближайшего представимого значения. Не:-)

Проблема точных данных не ограничивается C, поэтому давайте посмотрим дальше.

Немало рассуждений о редактировании стандартов, неточно, должно применяться к математическим операциям, которые требуют округления для представления результата с тем же типом. Например, схема имеет такое определение точности / неточности посредством точных операций и точных литеральных констант, см. R5RS §6. стандартные процедуры от http://www.schemers.org/Documents/Standards/R5RS/HTML

Для случая double x=0.1 мы либо считаем, что 0.1 - это четко определенный двойной литерал, либо, как в схеме, что литерал - это неточная константа, образованная неточной операцией времени компиляции (округление до ближайшего двойного результата операции 1/10, которая хорошо определена в Q). Таким образом, мы всегда в конечном итоге на операции.

Давайте сосредоточимся на +, остальные могут быть определены математически с помощью + и свойства группы.

Возможное определение неточности может быть таким:

If there exists any pair of values (a,b) of a type such that a+b-a-b != 0,
then this type is inexact (in the sense that + operation is inexact).

Для каждого представления с плавающей запятой, о котором мы знаем (тривиальный случай значений nan и inf), очевидно, существует такая пара, поэтому мы можем сказать, что float (операции) неточны.

Для четко определенной беззнаковой арифметической модели + является точным.

Для подписанного int у нас есть проблема UB в случае переполнения, поэтому нет гарантии точности... Если мы не уточним правило, чтобы справиться с этой сломанной арифметической моделью:

If there exists any pair (a,b) such that (a+b) is well defined
and a+b-a-b != 0,
then the + operation is inexact.

Выше определенность может помочь нам распространиться и на другие операции, но в этом нет необходимости.
Тогда мы должны были бы рассматривать случай / как ложный полиморфизм, а не неточность
(/ определяется как частное евклидова деления для int).

Конечно, это не официальное правило, достоверность этого ответа ограничивается усилием рационального мышления

В C++ тип int используется для представления математического целочисленного типа (т. Е. Одного из набора {..., -1, 0, 1, ...}). Из-за практического ограничения реализации язык определяет минимальный диапазон значений, который должен храниться этим типом, и все допустимые значения в этом диапазоне должны быть представлены без двусмысленности на всех известных архитектурах.

Стандарт также определяет типы, которые используются для хранения чисел с плавающей запятой, каждый со своим собственным диапазоном допустимых значений. То, что вы не найдете, это список действительных чисел с плавающей запятой. Опять же, из-за практических ограничений стандарт допускает аппроксимации этих типов. Многие пытаются сказать, что только числа, которые могут быть представлены стандартом IEEE с плавающей запятой, являются точными значениями для этих типов, но это не является частью стандарта. Хотя это правда, что реализация языка на двоичных компьютерах имеет стандарт представления двойного и плавающего чисел, в языке нет ничего, что говорило бы о том, что оно должно быть реализовано на двоичном компьютере. Другими словами, float не определен стандартом IEEE, стандарт IEEE является просто приемлемой реализацией. Таким образом, если бы существовала реализация, которая могла бы содержать любое значение в диапазоне значений, которые определяют double и float без правил округления или оценки, вы могли бы сказать, что is_exact верно для этой платформы.

Строго говоря, T не может быть вашим единственным аргументом, чтобы сказать, является ли тип "is_exact", но мы можем вывести некоторые другие аргументы. Поскольку вы, вероятно, используете бинарный компьютер со стандартным оборудованием и любой общедоступный компилятор C++, когда вы присваиваете двойное значение.1 (что находится в допустимом диапазоне для типов с плавающей запятой), это не то число, которое будет использовать компьютер использовать в расчетах с этой переменной. Он использует самое близкое приближение, как определено стандартом IEEE. Конечно, если вы сравниваете литерал с самим собой, ваш компилятор должен возвращать true, потому что стандарт IEEE довольно явный. Мы знаем, что компьютеры не имеют бесконечной точности, и поэтому вычисления, которые мы ожидаем иметь значение.1, не обязательно будут иметь такое же приблизительное представление, как буквальное значение. Введите страшное сравнение эпсилон.

Чтобы практически ответить на ваш вопрос, я бы сказал, что для любого типа, который требует сравнения эпсилонов для проверки приблизительного равенства, is_exact должен возвращать false. Если для этого типа достаточно строгого сравнения, оно должно вернуть true.

std::numeric_limits<T>::is_exact должно быть false если и только если T Определение допускает значения, которые могут быть недоступны для хранения.

C++ считает любой литерал с плавающей точкой допустимым значением для своего типа. И реализации могут решать, какие значения имеют точное хранимое представление.

Таким образом, для каждого действительного числа в допустимом диапазоне (например, 2.0 или же 0.2), C++ всегда обещает, что число является действительным double и никогда не обещает, что значение может быть сохранено точно.

Это означает, что два предположения, сделанные в этом вопросе - хотя и справедливо для вездесущего стандарта IEEE с плавающей запятой - неверны для определения C++:

Я уверен, что я мог бы хранить 2 в двойном точно.

Я мог бы тогда разделить [это] на 10 и [двойник не будет] точно содержать математический результат.

Определение, данное в стандарте C++, кажется довольно однозначным:

static constexpr bool is_exact;

True, если тип использует точное представление. Все целочисленные типы являются точными, но не все точные типы являются целочисленными. Например, рациональные и фиксированные представления являются точными, но не целочисленными.

Значимый для всех специализаций.

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