Потеря точности для преобразования int в float - PullRequest
0 голосов
/ 01 октября 2018

В C ++ преобразование целочисленного значения типа I в тип с плавающей запятой F будет точным - как static_cast<I>(static_cast<F>(i)) == i - если диапазон I является частью диапазона интегральных значенийиз F.

Возможно ли, и если да, то как рассчитать потерю точности в static_cast<F>(i) (без использования другого типа с плавающей запятой с более широким диапазоном)?

КакДля начала я попытался написать функцию, которая возвращала бы, если преобразование является безопасным или нет (безопасным, то есть без потери точности), но я должен признать, что не уверен в его правильности.

template <class F, class I>
bool is_cast_safe(I value)
{
    return std::abs(alue) < std::numeric_limits<F>::digits;
}

std::cout << is_cast_safe<float>(4) << std::endl; // true
std::cout << is_cast_safe<float>(0x1000001) << std::endl; // false

Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 01 октября 2018

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.
0 голосов
/ 01 октября 2018

I loss = abs(static_cast<I>(static_cast<F>(i))-i) должен сделать работу.Единственное исключение, если величина i велика, поэтому static_cast<F>(i) будет генерировать вне диапазона I F.

(я предполагал, что I abs(I) доступно)

...