Подтвердить что ты не робот

Следующий более высокий/нижний номер двойной точности IEEE

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

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

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

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

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

4b9b3361

Ответ 1

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

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

  • если вы пишете Fortran, у вас есть nearest встроенный

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

Ответ 2

Как говорит Торстен С., это можно сделать с классом 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,, но вы можете проверить их и разобраться с ними, как вам нравится, если вы беспокоитесь об этом.

Ответ 3

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

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

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

Math.nextUp:

public static double nextUp (double d):

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

Специальные случаи:

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

Параметры:

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

Возврат:

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

public static float nextUp (float f):

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

Специальные случаи:

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

Параметры:

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

Возврат:

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

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

Math.nextAfter:

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

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

Специальные случаи:

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

Параметры:

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

Возврат:

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

public static float nextAfter (запуск с плавающей точкой,                             двойное направление)

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

Специальные случаи:

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

Параметры:

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

Возврат:

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

Ответ 4

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

       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);
}

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

Ответ 5

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

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

Что не так, если просто увеличивать младший значащий бит в двоичном или шестнадцатеричном представлении?

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

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

Ответ 6

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

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