Как обойти некоторые ошибки округления? - PullRequest
2 голосов
/ 23 июня 2010

У меня есть метод, который работает с некоторыми географическими координатами в .NET, и у меня есть структура, в которой хранится пара координат, так что если 256 передается для одной из координат, она становится равной 0. Однако в одном конкретном случае вычисляется значение приблизительно 255,99999999 и, таким образом, сохраняется в структуре. Когда он печатается в ToString (), он становится 256, что не должно произойти - 256 должно быть 0. Я не возражаю, если он напечатает 255.9999998, но проблема в том, что он печатает 256, когда отладчик показывает 255.99999998. И хранить и отображать 0 было бы еще лучше.

В частности, существует проблема со сравнением. 255,999999 достаточно близко к 256, так что оно должно быть равно. Что я должен делать при сравнении двойников? использовать какое-нибудь значение эпсилона?


РЕДАКТИРОВАТЬ: В частности, моя проблема в том, что я беру значение, выполняю некоторые вычисления, затем выполняю противоположные вычисления для этого числа, и мне нужно точно вернуть исходное значение.

Ответы [ 4 ]

3 голосов
/ 23 июня 2010

Это звучит как проблема с тем, как печатается число, а не как оно сохраняется. A double имеет около 15 значащих цифр, поэтому он может отличить 255.99999998 от 256 с точностью до лишних.

1 голос
/ 23 июня 2010

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

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

Вышеприведенному вычислению, вероятно, суждено быть 256, если вы выполняете арифметику без потерь, как если бы вы использовали тип Rational.

Типы Rational могут идти по имени класса Ratio или Fraction, идовольно просто написать

Вот один пример .Вот еще один


Редактировать ....

Чтобы понять вашу проблему, учтите, что когда десятичное значение 0,01 преобразуется в двоичное представление, оно не может быть сохранено точнов конечной памяти.Шестнадцатеричное представление для этого значения равно 0,028F5C28F5C, где «28F5C» повторяется бесконечно.Поэтому даже перед выполнением каких-либо вычислений вы теряете точность, просто сохраняя 0,01 в двоичном формате.

Для решения этой проблемы используются классы Rational и Decimal, хотя и с затратами на производительность.Рациональные типы избегают этой проблемы, сохраняя числитель и знаменатель для представления вашей ценности.Десятичный тип использует двоичный кодированный десятичный код , формат , который может быть с потерями при делении, но может точно хранить общие десятичные значения.

Для ваших целей я по-прежнему предлагаю тип Rational.

1 голос
/ 23 июня 2010

Вы можете выбрать форматные строки, которые позволят вам отображать столько чисел, сколько вам нужно.

Обычный способ сравнить двойные числа на равенство - это вычесть их и посмотреть, меньше ли абсолютное значение, чем некоторыепредопределенный эпсилон, может быть 0,000001.

0 голосов
/ 24 июня 2010

Вы должны определиться с порогом, при котором два значения равны.Это равносильно использованию так называемых чисел с фиксированной точкой (в отличие от числа с плавающей запятой).Затем вы должны выполнить округление вручную.

Я бы выбрал какой-нибудь неподписанный тип с известным размером (например, uint32 или uint64, если они доступны, я не знаю .NET) и обработал его.как тип числа с фиксированной точкой мод 256.

Например.

typedef uint32 fixed;

inline fixed to_fixed(double d)
{
    return (fixed)(fmod(d, 256.) * (double)(1 << 24))
}

inline double to_double(fixed f)
{
    return (double)f / (double)(1 << 24);
}

или что-то более сложное, чтобы соответствовать соглашению о округлении (к ближайшему, к меньшему, к большему, к нечетному, к четному).8 старших фиксированных битов содержат целую часть, 24 младших бита содержат дробную часть.Абсолютная точность равна 2 ^ {- 24}.

Обратите внимание, что сложение и вычитание таких чисел естественным образом оборачивается на 256. Для умножения вам следует остерегаться.

...