следующий выше / ниже IEEE номер двойной точности - PullRequest
21 голосов
/ 07 августа 2009

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

Сложность заключается в том, что формат IEEE не является полностью однородным. Если использовать низкоуровневый код и фактически добавить его к младшему значащему биту, результирующий формат может не быть следующим доступным двойным. Это может быть, например, номер особого случая, такой как PositiveInfinity или NaN. Есть также субнормальные значения, которые я не утверждаю, что понимаю, но которые, кажется, имеют определенные битовые комбинации, отличные от «нормального» шаблона.

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

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

Есть ли способ получить следующее значение (без какого-либо цикла, который пытается добавить все меньшие и меньшие значения).

Ответы [ 6 ]

13 голосов
/ 09 августа 2009

Есть функции, доступные именно для этого, но они могут зависеть от того, какой язык вы используете. Два примера:

  • если у вас есть доступ к приличной математической библиотеке C99, вы можете использовать nextafter (и его варианты float и long double, nextafterf и nextafterl); или nexttoward семейство (которое принимает длинный дубль в качестве второго аргумента).

  • если вы пишете на Фортране, у вас есть nearest встроенная функция

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

8 голосов
/ 11 июля 2012

Большинство языков имеют встроенные или библиотечные функции для получения следующего или предыдущего числа с одинарной (32-битной) и / или двойной (64-битной) точностью.

Для пользователей 32-битной и 64-битной арифметики с плавающей запятой хорошее понимание основных конструкций очень полезно для избежания некоторых опасностей с ними. Стандарт IEEE применяется единообразно, но все же оставляет ряд деталей до разработчиков. Следовательно, универсальное решение для платформы, основанное на битовых манипуляциях с представлениями машинных слов, может быть проблематичным и может зависеть от таких вопросов, как порядок байтов и так далее. Хотя понимание всех мрачных деталей того, как он может или должен работать на битовом уровне, может продемонстрировать интеллектуальное мастерство, все же лучше использовать встроенное или библиотечное решение, которое адаптировано для каждой платформы и имеет универсальный API для поддерживаемых платформ.

Я заметил решения для C # и C ++. Вот некоторые из них для Java:

Math.nextUp:

public static double nextUp (double d):

  • Возвращает значение с плавающей точкой рядом с d в направлении положительная бесконечность. Этот метод семантически эквивалентен nextAfter (d, Double.POSITIVE_INFINITY); однако следующий реализация может выполняться быстрее, чем его эквивалентный вызов nextAfter.

Особые случаи:

  • Если аргумент равен NaN, результат равен NaN.
  • Если аргумент бесконечно положительный, результат положительный бесконечность.
  • Если аргумент равен нулю, результат равен Double.MIN_VALUE

Параметры:

  • d - начальное значение с плавающей точкой

Возвращает:

  • Смежное значение с плавающей точкой ближе к положительной бесконечности.

public static float nextUp (float f):

  • Возвращает значение с плавающей точкой рядом с f в направлении положительная бесконечность. Этот метод семантически эквивалентен nextAfter (f, Float.POSITIVE_INFINITY); однако следующий реализация может работать быстрее, чем эквивалентный вызов nextAfter.

Особые случаи:

  • Если аргумент равен NaN, результат равен NaN.
  • Если аргумент бесконечный положительный, результат положительный бесконечность.
  • Если аргумент равен нулю, результат равен Float.MIN_VALUE

Параметры:

  • f - начальное значение с плавающей точкой

Возвращает:

  • Смежное значение с плавающей точкой ближе к положительной бесконечности.

Следующие два более сложны в использовании. Однако направление к нулю или к положительной или отрицательной бесконечности представляется более вероятным и полезным. Другое использование состоит в том, чтобы видеть, что промежуточное значение существует между двумя значениями. Можно определить, сколько существует между двумя значениями с помощью цикла и счетчика. Кроме того, кажется, что они, наряду с методами nextUp, могут быть полезны для увеличения / уменьшения циклов for.

Math.nextAfter:

public static double nextAfter (двойной запуск, двойное направление)

  • Возвращает число с плавающей точкой рядом с первым аргументом в Направление второго аргумента. Если оба аргумента сравниваются как равно второй аргумент возвращается.

Особые случаи:

  • Если один из аргументов является NaN, то возвращается NaN.
  • Если оба аргумента являются знаковыми нулями, направление возвращается без изменений (как подразумевается требованием возврата второго аргумента, если аргументы сравниваются как равные).
  • Если начало равно ± Double.MIN_VALUE и направление имеет значение, такое что результат должен иметь меньшую величину, чем ноль с тем же знак как начало возвращено.
  • Если начало бесконечно, а направление имеет значение, такое, что результат должен иметь меньшую величину, Double.MAX_VALUE с тем же знаком как начало возвращается.
  • Если начало равно ± Double.MAX_VALUE и направление имеет значение так что результат должен иметь большую величину, бесконечность с возвращается тот же знак, что и в начале.

Параметры:

  • start - начальное значение с плавающей точкой
  • direction - значение, указывающее, какой из соседей начала или начала должен быть возвращен

Возвращает:

  • Число с плавающей точкой рядом с началом в направлении направление.

public static float nextAfter (начало плавания, двойное направление)

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

Особые случаи:

  • Если какой-либо аргумент является NaN, то возвращается NaN.
  • Если оба аргумента являются знаковыми нулями, значение эквивалентно направлению возвращается
  • Если начало равно ± Float.MIN_VALUE и направление имеет значение, такое что результат должен иметь меньшую величину, чем ноль с тем же знак как начало возвращается.
  • Если начало бесконечно, а направление имеет такое значение, что результат должен иметь меньшую величину, Float.MAX_VALUE с тем же знаком как начало возвращается.
  • Если начало равно ± Float.MAX_VALUE и направление имеет значение, такое что результат должен иметь большую величину, бесконечность с тем же знак как начало возвращается.

Параметры:

  • start - начальное значение с плавающей точкой
  • direction - значение, указывающее, какой из соседей или начала start должен быть возвращен

Возвращает:

  • Число с плавающей точкой рядом со стартом в направлении направления.
5 голосов
/ 17 февраля 2010

Как говорит Торстен С., это можно сделать с помощью класса BitConverter, но его метод предполагает, что метод DoubleToInt64Bits возвращает внутреннюю байтовую структуру double, чего нет. Целое число, возвращаемое этим методом, на самом деле возвращает число представимых двойных чисел от 0 до вашего. То есть наименьший положительный двойной представлен 1, следующий наибольший двойной - 2 и т. д. и т. д. Отрицательные числа начинаются с long.MinValue и уходят от 0d.

Так что вы можете сделать что-то вроде этого:

public static double NextDouble(double value) {

    // Get the long representation of value:
    var longRep = BitConverter.DoubleToInt64Bits(value);

    long nextLong;
    if (longRep >= 0) // number is positive, so increment to go "up"
        nextLong = longRep + 1L;
    else if (longRep == long.MinValue) // number is -0
        nextLong = 1L;
    else  // number is negative, so decrement to go "up"
        nextLong = longRep - 1L;

    return BitConverter.Int64BitsToDouble(nextLong);
}

Это не относится к Infinity и NaN,, но вы можете проверить их и обращаться с ними так, как вам нравится, если вы беспокоитесь об этом.

2 голосов
/ 08 декабря 2009

Да, есть способ. В C #:

       public static double getInc (double d)
        {
                // Check for special values
                if (double.IsPositiveInfinity(d) || double.IsNegativeInfinity(d))
                    return d;
                if (double.IsNaN(d))
                    return d;

                // Translate the double into binary representation
                ulong bits = (ulong)BitConverter.DoubleToInt64Bits(d);
                // Mask out the mantissa bits
                bits &= 0xfff0000000000000L;
                // Reduce exponent by 52 bits, so subtract 52 from the mantissa.
                // First check if number is great enough.
                ulong testWithoutSign = bits & 0x7ff0000000000000L;
                if (testWithoutSign > 0x0350000000000000L)
                  bits -= 0x0350000000000000L;
                else
                  bits = 0x0000000000000001L;
                return BitConverter.Int64BitsToDouble((long)bits);
}

Увеличение может быть добавлено и вычтено.

1 голос
/ 11 июля 2012

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

Для значений около (около относительно обширного диапазона десятичных значений, которые могут представлять двойные числа) 1 или -1, эпсилон будет нулевым или очень, очень маленьким. Для значений, которые постепенно приближаются к + или - бесконечности или нулю, эпсилон начнет расти. При значениях, очень близких к нулю или бесконечности, эпсилон будет очень большим, потому что доступные двоичные представления для десятичных значений в этих диапазонах очень и очень редки.

1 голос
/ 07 августа 2009

Я не уверен, что слежу за вашей проблемой. Конечно, стандарт IEEE является полностью однородным? Например, посмотрите на этот отрывок из статьи Википедии для чисел двойной точности.

3ff0 0000 0000 0000   = 1
3ff0 0000 0000 0001   = 1.0000000000000002, the next higher number > 1
3ff0 0000 0000 0002   = 1.0000000000000004

Что не так с простым приращением младшего значащего бита в двоичном или шестнадцатеричном представлении?

Что касается специальных чисел (бесконечность, NaN и т. Д.), Они четко определены, и их не так много. Пределы определяются аналогично.

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

...