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

Как изменить поплавок на наименьший приращение (или близко к нему)?

У меня есть значение double f и мне хотелось бы немного подтолкнуть его немного больше (или меньше), чтобы получить новое значение, которое будет как можно ближе к оригиналу, но все же строго больше (или меньше) оригинала.

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

4b9b3361

Ответ 1

Проверьте файл math.h. Если вам повезет, у вас есть nextafter и nextafterf. Они делают именно то, что вы хотите, с помощью портативного и независимого от платформы способа и являетесь частью стандарта C99.

Еще один способ сделать это (может быть откатным решением) - разложить ваш float на часть мантиссы и экспонента. Приращение легко: просто добавьте его к мантиссе. Если вы получаете переполнение, вам нужно справиться с этим, увеличивая показатель экспоненты. Decrementing работает одинаково.

EDIT. Как указано в комментариях, достаточно просто увеличить приращение в нем двоичного представления. Мантисса-переполнение увеличит экспоненту, и это именно то, что мы хотим.

Это в двух словах то же самое, что и в дальнейшем.

Это не будет полностью переносимым. Вам придется иметь дело с endianess и тем фактом, что не все машины имеют IEEE-поплавки (ok - последняя причина более академична).

Также обработка NAN и бесконечности может быть немного сложной. Вы не можете просто увеличивать их, поскольку они по определению не являются числами.

Ответ 2

u64 &x = *(u64*)(&f);
x++;

Да, серьезно.

Изменить: Как заметил кто-то, это не касается должностей -ve, Inf, Nan или переполнения. Более безопасная версия выше

u64 &x = *(u64*)(&f);
if( ((x>>52) & 2047) != 2047 )    //if exponent is all 1 then f is a nan or inf.
{
    x += f>0 ? 1 : -1;
}

Ответ 3

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

Ознакомьтесь с спецификацией IEEE для представления с плавающей запятой. Самый простой способ - переосмыслить значение как целочисленный тип, добавить 1, а затем проверить (если вам интересно), что вы не перевернули знак или не создали NaN, изучив биты знака и экспоненты.

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

Ответ 4

Мне нужно было сделать то же самое и придумал этот код:

double DoubleIncrement(double value)
{
  int exponent;
  double mantissa = frexp(value, &exponent);
  if(mantissa == 0)
    return DBL_MIN;

  mantissa += DBL_EPSILON/2.0f;
  value = ldexp(mantissa, exponent);
  return value;
}

Ответ 5

Для чего это стоит, значение, для которого стандартное приращение ++ перестает функционировать, составляет 9 007 199,254,740,992.

Ответ 6

Это может быть не совсем то, что вы хотите, но вы все равно можете найти numeric_limits. В частности, члены min() и epsilon().

Я не считаю, что что-то вроде mydouble + numeric_limits:: epsilon() будет делать то, что вы хотите, если mydouble уже близко к epsilon. Если да, то вам повезло.

Ответ 7

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

#include <stdio.h>

int main()
{
    /* two numbers to work with */
    double number1, number2;    // result of calculation
    double result;
    int counter;        // loop counter and accuracy check

    number1 = 1.0;
    number2 = 1.0;
    counter = 0;

    while (number1 + number2 != number1) {
        ++counter;
        number2 = number2 / 10;
    }
    printf("%2d digits accuracy in calculations\n", counter);

    number2 = 1.0;
    counter = 0;

    while (1) {
        result = number1 + number2;
        if (result == number1)
            break;
        ++counter;
        number2 = number2 / 10.0;
    }

    printf("%2d digits accuracy in storage\n", counter );

    return (0);
}