Есть ли решение проблем с арифметикой с плавающей запятой в C++?

StackOverflow https://stackoverflow.com/questions/4043987

Вопрос

Я занимаюсь арифметикой с плавающей запятой и испытываю проблемы с точностью.Результирующее значение различно на двух машинах для одного и того же ввода.Я прочитал пост @ Почему я не могу умножить число с плавающей запятой? а также прочитал другие материалы в Интернете и понял, что это связано с двоичным представлением чисел с плавающей запятой и машинным эпсилоном.Однако я хотел проверить, есть ли способ решить эту проблему/Некоторые обходные пути для арифметики с плавающей запятой в C++??Я конвертирую число с плавающей запятой в беззнаковое короткое для хранения и при необходимости конвертирую обратно.Однако, когда я конвертирую его обратно в беззнаковое короткое, точность (до 6 десятичных знаков) остается правильной на одной машине, но не работает на другой.

//convert FLOAT to short

unsigned short sConst = 0xFFFF;

unsigned short shortValue = (unsigned short)(floatValue * sConst);

//Convert SHORT to FLOAT

float floatValue = ((float)shortValue / sConst);
Это было полезно?

Решение

А short должно быть не менее 16 бит, и во многих реализациях именно так и есть.Ан unsigned 16-битный short будет содержать значения от 0 до 65535.Это означает, что сокращение не будет содержать полных пяти цифр точности и уж точно не шести.Если вам нужны шесть цифр, вам нужно 20 бит.

Таким образом, любая потеря точности, скорее всего, связана с тем, что вы пытаетесь упаковать шесть цифр точности в нечто меньшее, чем пять цифр.Нет другого решения этой проблемы, кроме использования целочисленного типа, который, вероятно, занимает столько же места, сколько и float.

Я не знаю, почему это может работать в одной конкретной системе.Вы использовали одни и те же номера в обоих случаях?Использовалась ли какая-нибудь старая система с плавающей запятой, которая по совпадению дала ожидаемые результаты на опробованных вами образцах?Возможно ли было использовать более крупный short чем другой?

Другие советы

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

Точное определение слова «слишком много» полностью зависит от вашего приложения.Например, если вы вычислите a + b на разных платформах вы должны обнаружить, что два результата находятся в пределах машинной точности друг друга.С другой стороны, если вы делаете что-то более сложное, например инверсию матрицы, результаты, скорее всего, будут отличаться не только точностью машины.Точное определение того, насколько близки результаты друг к другу, является очень тонким и сложным процессом.Если вы точно не знаете, что делаете, вероятно, безопаснее (и разумнее) определить необходимую вам степень точности в дальнейшем в вашем приложении и убедиться, что результат достаточно точен.

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

Функции сравнения с плавающей запятой для C#

Вместо использования 0xFFFF используйте половину, т.е.32768 для конвертации.32768 (Ox8000) имеет двоичное представление 1000000000000000, тогда как OxFFFF имеет двоичное представление 1111111111111111.Двоичное представление Ox8000 ясно подразумевает, что операции умножения и деления во время преобразования (в короткое (или) при обратном преобразовании в число с плавающей запятой) не изменят значения точности после нуля.Однако для одностороннего преобразования предпочтительнее использовать OxFFFF, поскольку это приводит к более точному результату.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top