std::strod()
преобразует строку в двойную Но в одном конкретном случае есть небольшая десятичная ошибка. Эта ошибка также может быть замечена с atof()
а также sscanf()
, Ошибка возникает только если все следующие условия выполняются:
_controlfp_s()
установить «округление до минус бесконечности»(Я тестировал только на 64-битной Windows 8.1 Enterprise)
Простой пример здесь:
#include <string>
inline double fromString(const std::string& s) {
size_t idx;
return std::stod(s, &idx);
}
int main()
{
double res1 = fromString("0.5");
unsigned tmp;
_controlfp_s(&tmp, RC_DOWN, MCW_RC);
double res2 = fromString("0.5");
_controlfp_s(&tmp, RC_NEAR, MCW_RC);
double res3 = fromString("0.5");
}
std::stod()
звонит std::calling strtod()
в stdlib.h.
res1
будет ровно 0,5, но res2
будет 0.50000000000000011
_controlfp_s(&tmp, RC_DOWN, MCW_RC);
управляет ошибками округления, в этом случае он установлен на округление до минус бесконечности.
Если он установлен по умолчанию с _controlfp_s(&tmp, RC_NEAR, MCW_RC);
затем strod()
опять точно, так res3
0,5
Обратите внимание, что некоторые десятичные числа не могут быть представлены точно. Но некоторые цифры могут, например, 2,5, 0,5 и 0,375, но все они получают ошибку округления в приведенном выше примере.
Странно, не правда ли?
Я делаю что-то не так, или это ошибка в стандартной библиотеке Visual Studio?
Благодаря ссылке от Адриана Маккарти, я нашел обходной путь: использовать
fesetround()
вместо _controlfp_s()
:
#if _MSC_VER == 1900
//Visual Studio 2015
fesetround(FE_DOWNWARD);
#else
_controlfp_s(&tmp, RC_DOWN, MCW_RC);
#endif
fesetround()
был представлен в VS2015.
Я сообщу об ошибке в Microsoft.
Можно также использовать #if _MSC_VER == 1900 && defined(_WIN64)
чтобы быть более точным, так как ошибка не существует для 32-битных целей.
Это не ошибка. Это особенность!
https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html