x64 visual studio redistribuibles para paquetes microsoft descargar c++ visual-c++ c++11 visual-studio-2012

redistribuibles - Comportamiento diferente de fmod(y otros) bajo c++ 11, en Visual Studio al menos



microsoft visual c++2013 (2)

Como Christian ha señalado en su pregunta y respuesta vinculadas, este es el comportamiento que exige una lectura estricta del Estándar.

Sin embargo, puede solucionar esto para todas las versiones con bastante facilidad:

Num fmod(const Num a, const Num b) { const double cvt_a = a; const double cvt_b = b; return Num(fmod(cvt_a, cvt_b)); }

Tengo un código de ejemplo que se comporta de manera diferente en Visual C ++ 2012 con los nuevos encabezados de C ++ 11 de lo que solía hacerlo en VC ++ 2010. Tiene que ver con lo que sucede cuando se llama a la función std :: fmod que se obtiene cuando se incluye cmath. y cuando los argumentos que aprueba no son dobles, sino que son clases que tienen una conversión implícita a doble operador:

#include <cmath> class Num { double d_; public: Num(double d) : d_(d) {} operator double() const { return d_; } }; int main(int argc, char* argv[]) { Num n1(3.14159265358979323846264338327950288419716939937510); Num n2(2.0); double result1 = fmod((double)n1, (double)n2); double result2 = fmod((float)n1, (float)n2); double result3 = fmod(n1, n2); if (result2==result1) std::cout << "fmod(double, double) returns the same as fmod(float,float)" << std::endl; if (result3==result1) std::cout << "fmod(Num, Num) returns the same as fmod(double,double)" << std::endl; if (result3==result2) std::cout << "fmod(Num, Num) returns the same as fmod(float,float)" << std::endl; }

Para mi sorpresa, esto llama a la versión de fmod que toma dos flotadores, en lugar de la versión de fmod que toma dos dobles.

Entonces mi pregunta es, ¿es este el comportamiento correcto dado el estándar C ++ 11? La única información que puedo encontrar sobre el comportamiento se encuentra en la documentación de cppreference.com aquí , que dice (énfasis mío):

Si algún argumento tiene tipo integral, se convierte en doble. Si cualquier otro argumento es largo doble, entonces el tipo de retorno es largo doble, de lo contrario es doble .

Sin embargo, la implementación en los archivos de encabezado de Visual Studio parecería implementar "de lo contrario es un flotante".

Alguien sabe cuál es la intención :-)?

Habiendo ejecutado el ejemplo a través de una versión en línea de C ++ 11 de GCC (no tengo acceso simple a una copia reciente de GCC) parecería estar llamando a la versión "doble" de fmod, que es lo que ingenuamente espero .

Para mayor claridad, estoy usando

Microsoft (R) C / C ++ Optimizing Compiler versión 17.00.51106.1 para x86

que es lo que viene con

Microsoft Visual Studio Express 2012 para Windows Desktop Versión 11.0.51106.01 Actualización 1


Esto está relacionado con esta pregunta mía . La razón es que, para proporcionar las sobrecargas adicionales que exige la norma (y se citan en su pregunta) VS 2012 define las plantillas de funciones generales para todas las funciones matemáticas de 2 argumentos. Entonces, en realidad no llama a fmod(float, float) sino fmod<Num>(Num, Num) en primer lugar.

La razón por la cual esta función de plantilla es preferible a la double es porque la versión doble requeriría una conversión definida por el usuario de Num a double , mientras que la versión de la plantilla es directamente instanciable.

Pero el tipo fundamental real para llamar a la función fmod está determinado por este rasgo de tipo de <xtgmath.h> :

template<class _Ty> struct _Promote_to_float { // promote integral to double typedef typename conditional<is_integral<_Ty>::value, double, _Ty>::type type; }; template<class _Ty1, class _Ty2> struct _Common_float_type { // find type for two-argument math function typedef typename _Promote_to_float<_Ty1>::type _Ty1f; typedef typename _Promote_to_float<_Ty2>::type _Ty2f; typedef typename conditional<is_same<_Ty1f, long double>::value || is_same<_Ty2f, long double>::value, long double, typename conditional<is_same<_Ty1f, double>::value || is_same<_Ty2f, double>::value, double, float>::type>::type type; };

Lo que hace es verificar el tipo promocionado _Promote_to_float (que en su caso vuelve a ser Num , porque solo comprueba si su integral, que Num claramente no es) para todos los tipos de coma flotante hasta que coincida, que no lo hace y por lo tanto da como resultado el caso else de float .

La razón de este comportamiento erróneo es que esas sobrecargas matemáticas adicionales nunca se proporcionaron para todos y cada uno de los tipos, sino solo para los tipos aritméticos incorporados (y la redacción estándar ambigua está a punto de solucionarse, como se afirma en mi respuesta a la pregunta vinculada). Entonces, en todo este tipo de mecánica de deducción explicada anteriormente, VS 2012 supone que los tipos pasados ​​son tipos integrales incorporados o, si no, tipos de punto flotante incorporados, que por supuesto falla para Num . Entonces, el problema real es que VS proporciona funciones matemáticas demasiado genéricas, mientras que solo deberían proporcionar sobrecargas para los tipos integrados (como ya se hizo correctamente para las funciones de 1 argumento). Como se indica en la respuesta vinculada, ya archivé un error para esto.

EDITAR: De hecho (como también se dio cuenta), incluso si siguieran la redacción estándar actualmente ambigua y se necesitaran plantillas de funciones genéricas, aún deberían haber definido el tipo promovido real de esos argumentos genéricos como double lugar de float . Pero creo que el problema real aquí es que ignoran por completo la posible presencia de tipos no integrados en todo este proceso de conversión (ya que para los tipos integrados su lógica funciona perfectamente bien).

Pero de acuerdo con la redacción estándar ambigua actual (que ya está planeada para cambiar, sin embargo) en la sección 26.8 [c.math] , de hecho están deduciendo correctamente el tipo promocionado como float (por el 3er caso):

habrá sobrecargas adicionales suficientes para garantizar:

  1. Si cualquier argumento correspondiente a un parámetro doble tiene un tipo double long, entonces todos los argumentos correspondientes a los parámetros dobles se convierten efectivamente en double long.
  2. De lo contrario, si cualquier argumento correspondiente a un parámetro doble tiene un tipo double o un entero, todos los argumentos correspondientes a los parámetros dobles se convierten efectivamente en dobles.
  3. De lo contrario, todos los argumentos correspondientes a los parámetros dobles se emiten efectivamente para flotar.