Std:: exp of float отрицательная бесконечность возвращает отрицательную бесконечность для сборки x64 в Visual С++ 2013

Использование std::exp для вычисления e^-infinity возвращает -infinity при использовании представления float бесконечности и построения двоичного кода x64 с использованием Visual С++ 2013. Я ожидаю, что он вернет 0, что происходит с сборками Win32 или версия std::exp, которая принимает double.

Следующий код, построенный как x64, демонстрирует проблему.

#include <limits>
#include <iostream>

int main(const int argc, const char** argv) {
    std::cout << "exp of float -infinity: " << std::exp(-std::numeric_limits<float>::infinity()) << std::endl;
    std::cout << "exp of double -infinity: " << std::exp(-std::numeric_limits<double>::infinity()) << std::endl;
}

Параметры командной строки для компиляции (взяты из Visual Studio):

/GS /Wall /Gy /Zc:wchar_t /Zi /Gm- /Od /sdl /Fd"x64\Release\vc120.pdb" /fp:precise /D "_MBCS" /errorReport:prompt /WX /Zc:forScope /Gd /Oi /MD /Fa"x64\Release\" /EHsc /nologo /Fo"x64\Release\" /Fp"x64\Release\NumericLimitsTest.pch" 

Вывод выше:

exp of float -infinity: -1.#INF
exp of double -infinity: 0

Почему это происходит?

Ответ 1

Я бы сказал, что это было ошибкой некоторого описания, так как С++ 11 отзывается на C99 для cmath функциональности, а C99 четко заявляет в F.9.3.1, что exp(−∞) returns +0. Однако имейте в виду, что в приложении к стандарту, который гласит:

Реализация, которая определяет __STDC_IEC_559__, должна соответствовать спецификациям в этом приложении.

Этот макрос не может быть определен в 32- или 64-разрядном режиме в MSVC, поэтому, вероятно, это не ошибка, и вам может быть не повезло. Также изменение режима с плавающей запятой между /fp:strict и /fp:precise не делает ничего лучше.

Во всех случаях результат, похоже, отличается между 32-битным и 64-битным целевым и, основываясь на стандарте, который только утверждает, что exp будет compute the base-e exponential of x с кажущимся отсутствием требования о том, как, кажется, все в порядке.


Если вы после быстрого исправления, функция pow, кажется, генерирует правильные результаты:

#define DBL_E 2.71828182845904523536
#define FLT_E 2.71828182845904523536f
std::cout
    << "exp of float -infinity: "
    << std::pow(FLT_E, -std::numeric_limits<float>::infinity())
    << std::endl;
std::cout
    << "exp of double -infinity: "
    << std::pow(DBL_E,-std::numeric_limits<double>::infinity())
    << std::endl;

Это генерирует ноль для обеих строк, независимо от того, есть ли у вас 64-bit/32-bit, debug/release или fp:precise/fp:strict, но не гарантировано ли это.