Рассчитать единицу на последнем месте (ULP) для двойников - PullRequest
8 голосов
/ 28 февраля 2012

Есть ли в .NET встроенный метод для вычисления ULP для данного типа double или float?

Если нет, то какой самый эффективный способ сделать это?

Ответы [ 2 ]

6 голосов
/ 28 февраля 2012

Кажется, функция довольно тривиальна;это основано на псевдокоде в принятом ответе на вопрос, связанный с помощью vulkanino:

double value = whatever;
long bits = BitConverter.DoubleToInt64Bits(value);
double nextValue = BitConverter.Int64BitsToDouble(bits + 1);
double result = nextValue - value;

Для чисел с плавающей запятой вам необходимо предоставить собственную реализацию SingleToInt32Bits и Int32BitsToSingle, поскольку BitConverter нене имеют этих функций.

Эта страница показывает особые случаи в реализации функции Java;работать с ними тоже должно быть довольно тривиально.

3 голосов
/ 23 мая 2013

ответ phoog хорошо, но имеет недостатки с отрицательными числами, max_double, infinity и NaN.

phoog_ULP (положительный х) -> положительное число. Хорошо.
phoog_ULP (минус х) -> отрицательное число. Я бы ожидал положительного числа.
Чтобы исправить это, я рекомендую вместо этого:

long bits = BitConverter.DoubleToInt64Bits(value) & 0x7FFFFFFFFFFFFFFFL;

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

phoog_ULP (x = +/- Max_double 1.797 ... e + 308) возвращает бесконечный результат. (+1.996 ... e + 292) ожидается.
phoog_ULP (x = +/- Infinity) приводит к NaN. + Бесконечность ожидается.
phoog_ULP (x = +/- NaN) может неожиданно измениться с sNan на qNaN. Никаких изменений не ожидается. В любом случае можно утверждать, что знак должен стать + в этом случае.


Чтобы решить эти проблемы, я вижу только короткую серию брутальных тестов if (), чтобы приспособиться к ним, возможно, на значении «биты» для целесообразности. Пример:

double ulpc(double value) {
  long long bits = BitConverter::DoubleToInt64Bits(value);
  if ((bits & 0x7FF0000000000000L) == 0x7FF0000000000000L) { // if x is not finite
    if (bits & 0x000FFFFFFFFFFFFFL) { // if x is a NaN
      return value;  // I did not force the sign bit here with NaNs.
      } 
    return BitConverter.Int64BitsToDouble(0x7FF0000000000000L); // Positive Infinity;
    }
  bits &= 0x7FFFFFFFFFFFFFFFL; // make positive
  if (bits == 0x7FEFFFFFFFFFFFFFL) { // if x == max_double (notice the _E_)
    return BitConverter.Int64BitsToDouble(bits) - BitConverter.Int64BitsToDouble(bits-1);
  }
  double nextValue = BitConverter.Int64BitsToDouble(bits + 1);
  double result = nextValue - fabs(value);
}
...