Реализовать функцию Matlab eps(x) на С++

Я пытаюсь реализовать функцию Matlab eps(x) на С++.

Например, в Матлабе:

>> eps(587.3888)
ans = 1.1369e-13
>> eps(single(587.3888))
ans = 6.1035e-05

Однако, когда я пытаюсь сделать это на С++, я не могу получить правильный ответ с одинарной точностью.

#include <limits>
#include <iostream>
#include <math.h>

#define DEBUG(x) do { std::cerr << x << std::endl; } while (0)
#define DEBUG2(x) do { std::cerr << #x << ": " << x << std::endl; } while (0)

int main() {

    float epsf = std::numeric_limits<float>::epsilon();
    DEBUG2(epsf);
    double epsd = std::numeric_limits<double>::epsilon();
    DEBUG2(epsd);

    float espxf = nextafter(float(587.3888), epsf) - float(587.3888);
    double espxd = nextafter(double(587.3888), epsd) - double(587.3888);
    DEBUG2(espxf);
    DEBUG2(espxd);

}

Запустив программу, я получаю следующий вывод:

$ ./a.out 
epsf: 1.19209e-07
epsd: 2.22045e-16
espxf: -1.13687e-13
espxd: -1.13687e-13

Кажется, что по какой-то причине, хотя значения eps для одинарной и двойной точности верны, вывод с использованием функции nextafter выводит только значение двойной точности. Мое значение для epsxf должно быть 6.1035e-05, как в Matlab.

Есть предположения?


person kyle    schedule 23.11.2013    source источник
comment
MATLAB eps всегда дает положительные результаты. Приведенный выше код даст отрицательный результат, если x больше epsf. Вот фиксированный код: double eps(float x) { float xp = std::abs(x); double x1 = std::nextafter(xp, xp + 1.0f); return x1 - xp; }   -  person legends2k    schedule 28.12.2015


Ответы (2)


Включите <cmath> и вызовите std::nextafter, и ваш код будет работать, если у вас есть компилятор C++11.

Включение <math.h> и вызов ::nextafter вызывает C-версию функции. Реализация nextafter на C, очевидно, не поддерживает перегрузки, поэтому C предоставляет nextafterf для результата с одинарной точностью, а также nextafterl для результата с четырехкратной точностью. (Простой вызов двойной точности nextafter с float не работает, потому что аргумент преобразуется в double.) Если у вас нет компилятора C++11, вы можете исправить свой код, вызвав ::nextafterf.

person user4815162342    schedule 23.11.2013

Используйте библиотеки. Функция Matlab eps на других языках называется ULP, поскольку единица стоит на последнем месте. . Согласно статье Википедии на ULP, следующая функция из библиотека boost C++ может использоваться для вычисления расстояния с плавающей запятой между двумя числами типа double a и b:

boost::math::float_distance(a, b)

Документация для float_distance: здесь.

person horchler    schedule 23.11.2013
comment
да, спасибо, я знал о реализации boost, однако для нашего класса мы не можем использовать библиотеки boost. - person kyle; 24.11.2013
comment
Мне действительно интересно, как в мире этот ответ оценивает отрицательный голос. Я не только объяснил концепцию ULP, но и предложил надежное альтернативное решение, которое работает как для одинарной, так и для двойной точности. ОП не указал на свое желание не использовать наддув. - person horchler; 24.11.2013