почему реализация floor, ceil возвращает x + x, когда x равен NaN или inf? - PullRequest
5 голосов
/ 04 апреля 2019

Я читаю реализацию математических функций IEEE-754 в glibc. Вот реализация floor.

float
__floorf(float x)
{
    int32_t i0,j0;
    uint32_t i;
    GET_FLOAT_WORD(i0,x);
    j0 = ((i0>>23)&0xff)-0x7f;
    if(j0<23) {
        if(j0<0) {
        /* return 0*sign(x) if |x|<1 */
        if(i0>=0) {i0=0;}
        else if((i0&0x7fffffff)!=0)
          { i0=0xbf800000;}
        } else {
        i = (0x007fffff)>>j0;
        if((i0&i)==0) return x; /* x is integral */
        if(i0<0) i0 += (0x00800000)>>j0;
        i0 &= (~i);
        }
    } else {
        if(__builtin_expect(j0==0x80, 0)) return x+x; /* inf or NaN */
        else return x;      /* x is integral */
    }
    SET_FLOAT_WORD(x,i0);
    return x;
}

Интересная часть - if(__builtin_expect(j0==0x80, 0)) return x+x; /* inf or NaN */. Почему он возвращает x+x, когда x - это inf или NaN? Почему бы просто не вернуть x?

EDIT

Я получил свой код от https://github.com/lattera/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/sysdeps/ieee754/flt-32/s_floorf.c и предположил, что это ответвление от glibc.

Ответы [ 2 ]

5 голосов
/ 04 апреля 2019

Цель состоит в том, чтобы поднять исключения.Когда вход в floor является сигнальным NaN, подпрограмма должна вызвать плавающую точку недопустимая операция исключение. 1 Вместо вызова некоторой подпрограммы, которая будет делать это путем манипулирования битами врегистр состояния с плавающей точкой, проще оценить x+x, так как добавление сигнального NaN к себе (или чему-либо еще) вызовет недопустимую операцию исключение.

Это довольнораспространенный в реализациях программ математической библиотеки.Для другого примера рассмотрим sin(x).Для очень маленьких значений x, sin(x) настолько близко к x, что x является ближайшим значением, представимым в формате с плавающей запятой, поэтому возвращаемое значение должно быть x.Но точный математический грех x не совсем x (если x не равен нулю), поэтому должно возникать исключение неточное .Для этого процедура может вернуть, например, x + x*x.Если x очень мало (но не равно нулю), это будет равняться x, но вызовет исключение недопустимое .

Обратите внимание на дополнительное преимущество в этом случае: Когда x равен нулю, x + x*x не вызывает исключение неточное .Таким образом, выражение служит как для нулевого, так и для очень малого ненулевого случая.Таким образом, он заменяет не только ручное создание исключения, но также и ветвление в зависимости от того, равен x ноль или нет.Это не редкость в этих выражениях;они являются эффективным способом реализации функции.

Сноска

1 Исключения с плавающей точкой не являются исключениями C ++.Как они обрабатываются, зависит от настроек среды с плавающей запятой.Чаще всего они просто поднимают флаги, которые программа может позже проверить.Но они также могут вызывать ловушки, которые изменяют выполнение программы, например исключения C ++.

0 голосов
/ 04 апреля 2019

Я не уверен, но я считаю, что это как-то связано с обработкой исключений: если x слишком велико, то return x+x может сгенерировать исключение. Хотя просто return x вообще не вызовет никаких исключений, и вы можете подумать, что все в порядке, хотя на самом деле вы работаете с ошибочными числами, либо потому, что они выходят за пределы нормального диапазона нумерации (inf), либо потому что они просто не правы (NaN).

...