is_cast_safe
может быть реализовано с помощью:
static const F One = 1;
F ULP = std::scalbn(One, std::ilogb(value) - std::numeric_limits<F>::digits + 1);
I U = std::max(ULP, One);
return value % U;
Устанавливает ULP
в значение позиции с наименьшей цифрой в результате преобразования value
в F
.logbf
возвращает позицию (как показатель степени с плавающей запятой) для позиции с самой высокой цифрой, а вычитание на единицу меньше, чем количество цифр, подстраивается к позиции с самой низкой цифрой.Тогда scalbn
дает нам значение этой позиции, которая является ULP.
Тогда value
может быть точно представлено в F
тогда и только тогда, когда оно кратно ULP.Чтобы проверить это, мы конвертируем ULP в I
(но подставляем 1, если оно меньше 1), а затем берем остаток от value
, деленный на ULP (или 1).
Также,если кто-то обеспокоен тем, что преобразование в F
может быть переполнено, код также может быть вставлен для обработки этого.
Расчет фактической суммы изменения более сложен.Преобразование в число с плавающей запятой может округляться в большую или меньшую сторону, и правило выбора определяется реализацией, хотя округление до ближайшего связывания с четным является обычным явлением.Таким образом, фактическое изменение не может быть вычислено из свойств с плавающей запятой, которые мы даем в numeric_limits
.Это должно включать выполнение преобразования и выполнение некоторой работы с плавающей запятой.Это определенно можно сделать, но это неприятно.Я думаю, что подход, который должен работать:
- Предположим,
value
неотрицателен.(Отрицательные значения могут обрабатываться аналогично, но пока для простоты здесь опущены.) - Во-первых, проверьте на переполнение при преобразовании в
F
.Само по себе это сложно, так как поведение не определено, если значение слишком велико.Некоторые подобные соображения были рассмотрены в этом ответе на вопрос о безопасном преобразовании из числа с плавающей запятой в целое (в C). - Если значение не переполняется, преобразуйте его.Пусть результат будет
x
.Разделите x
на основание с плавающей точкой r
, получив y
.Если y
не является целым числом (которое можно проверить с помощью fmod
или trunc
), преобразование было точным. - В противном случае конвертируйте
y
в I
, получая z
.Это безопасно, потому что y
меньше, чем исходное value
, поэтому оно должно соответствовать I
. - Тогда ошибка из-за преобразования составляет
(z-value/r)*r + value%r
.