Как я могу указать, в каком направлении округлить среднее значение двух поплавков, отличающихся LSB их значения?


Я работаю над рутиной оптимизации Нельдера-МИДа в C, которая включает в себя взятие среднего значения двух float s. в редких (но вполне воспроизводимых) обстоятельствах эти два float s, скажем x и y, отличаются только наименее значимым битом их значения. Когда берется среднее, ошибки округления подразумевают, что результат будет либо x, либо y.

Я хотел бы указать, что округление всегда должно быть в сторону второго float. То есть я не могу просто указать, что округление должно быть к нулю или бесконечности, потому что я заранее не знаю, будет ли x больше, чем y.

(Как) я могу это сделать?

2 5

2 ответа:

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

double average(double x, double y) {
    double a = 0.5*(x+y);
    return (a == x) ? y : a;
}

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

Интерес представляют следующие значения:

  • Когда значения имеют один и тот же знак и показатель степени и отличаются только на единицу в мантиссе.

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

Фактически, если вы используете номера IEEE-754 (что, вероятно, так и есть) вы можете выполнить оба теста сразу (после проверки таких вещей, как Zero, Inf и Nan):

if (   repr1 + 1 == repr2
    || repr2 + 1 == repr1)
  ....

Причина этого в том, что экспонента помещается прямо рядом с мантиссой, и если мантисса-все единицы, то сложение продолжится в поле экспоненты.

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