programas programacion principiantes paso para lenguaje ejemplos dev comandos basicos c++ floating-point rounding

programacion - redondo() para flotar en C++



lenguaje c++ ejemplos (20)

Necesito una función de redondeo de punto flotante simple, así:

double round(double); round(0.1) = 0 round(-0.1) = 0 round(-0.9) = -1

Puedo encontrar ceil() y floor() en math.h, pero no round() .

¿Está presente en la biblioteca estándar de C ++ con otro nombre, o falta?


Boost ofrece un conjunto simple de funciones de redondeo.

#include <boost/math/special_functions/round.hpp> double a = boost::math::round(1.5); // Yields 2.0 int b = boost::math::iround(1.5); // Yields 2 as an integer

Para obtener más información, consulte la documentación de Boost .

Edición : Desde C ++ 11, hay std::round , std::lround , y std::llround .


Cuidado con el floor(x+0.5) . Esto es lo que puede suceder con los números impares en el rango [2 ^ 52,2 ^ 53]:

-bash-3.2$ cat >test-round.c <<END #include <math.h> #include <stdio.h> int main() { double x=5000000000000001.0; double y=round(x); double z=floor(x+0.5); printf(" x =%f/n",x); printf("round(x) =%f/n",y); printf("floor(x+0.5)=%f/n",z); return 0; } END -bash-3.2$ gcc test-round.c -bash-3.2$ ./a.out x =5000000000000001.000000 round(x) =5000000000000001.000000 floor(x+0.5)=5000000000000002.000000

Esto es http://bugs.squeak.org/view.php?id=7134 . Usa una solución como la de @konik.

Mi propia versión robusta sería algo como:

double round(double x) { double truncated,roundedFraction; double fraction = modf(x, &truncated); modf(2.0*fraction, &roundedFraction); return truncated + roundedFraction; }

Otra razón para evitar el piso (x + 0.5) se da here .


El estándar C ++ 03 se basa en el estándar C90 para lo que el estándar llama la biblioteca estándar de C, que se trata en el borrador del estándar C ++ 03 (el borrador más cercano al C ++ 03 del proyecto público más cercano es N1804 ), sección 1.2 Referencias normativas :

La biblioteca descrita en la cláusula 7 de ISO / IEC 9899: 1990 y la cláusula 7 de ISO / IEC 9899 / Amd.1: 1995 se denomina en lo sucesivo la biblioteca C estándar. 1)

Si vamos a la documentación de C para round, lround, llround en cppreference podemos ver que round y las funciones relacionadas son parte de C99 y, por lo tanto, no estarán disponibles en C ++ 03 o anteriores.

En C ++ 11 esto cambia, ya que C ++ 11 se basa en el borrador estándar de C99 para la biblioteca estándar de C y, por lo tanto, proporciona std :: round y para los tipos de retorno integral std :: lround, std :: llround :

#include <iostream> #include <cmath> int main() { std::cout << std::round( 0.4 ) << " " << std::lround( 0.4 ) << " " << std::llround( 0.4 ) << std::endl ; std::cout << std::round( 0.5 ) << " " << std::lround( 0.5 ) << " " << std::llround( 0.5 ) << std::endl ; std::cout << std::round( 0.6 ) << " " << std::lround( 0.6 ) << " " << std::llround( 0.6 ) << std::endl ; }

Otra opción también de C99 sería std::trunc que:

Calcula el entero más cercano que no sea mayor en magnitud que arg.

#include <iostream> #include <cmath> int main() { std::cout << std::trunc( 0.4 ) << std::endl ; std::cout << std::trunc( 0.9 ) << std::endl ; std::cout << std::trunc( 1.1 ) << std::endl ; }

Si necesita admitir aplicaciones que no sean de C ++ 11, lo mejor sería utilizar boost round, iround, lround, llround o boost trunc .

Rodar tu propia versión de round es difícil

Probablemente no valga la pena hacer el esfuerzo de blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1 tuyo, ya que es más blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1 , redondear el flotante al entero más cercano, parte 2 y redondear el flotante al entero más cercano, la parte 3 explica:

Por ejemplo, un rollo común en su implementación usando std::floor y agregar 0.5 no funciona para todas las entradas:

double myround(double d) { return std::floor(d + 0.5); }

Una entrada por la que fallará es 0.49999999999999994 , ( verlo en vivo ).

Otra implementación común implica convertir un tipo de punto flotante en un tipo integral, que puede invocar un comportamiento indefinido en el caso de que la parte integral no se pueda representar en el tipo de destino. Podemos ver esto en el borrador de la sección 4.9 C ++, conversiones integrales flotantes que dice ( énfasis mío ):

Un prvalue de un tipo de punto flotante se puede convertir en un prvalue de un tipo entero. La conversión trunca; es decir, se desecha la parte fraccionaria. El comportamiento no está definido si el valor truncado no se puede representar en el tipo de destino. [...]

Por ejemplo:

float myround(float f) { return static_cast<float>( static_cast<unsigned int>( f ) ) ; }

Dado std::numeric_limits<unsigned int>::max() es 4294967295 entonces la siguiente llamada:

myround( 4294967296.5f )

causará desbordamiento, ( verlo en vivo ).

Podemos ver lo difícil que es esto al ver esta respuesta a la forma concisa de implementar round () en C? que hace referencia a la versión newlibs de una sola ronda de precisión flotante. Es una función muy larga para algo que parece simple. Parece poco probable que alguien sin un conocimiento íntimo de las implementaciones de punto flotante pueda implementar correctamente esta función:

float roundf(x) { int signbit; __uint32_t w; /* Most significant word, least significant word. */ int exponent_less_127; GET_FLOAT_WORD(w, x); /* Extract sign bit. */ signbit = w & 0x80000000; /* Extract exponent field. */ exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127; if (exponent_less_127 < 23) { if (exponent_less_127 < 0) { w &= 0x80000000; if (exponent_less_127 == -1) /* Result is +1.0 or -1.0. */ w |= ((__uint32_t)127 << 23); } else { unsigned int exponent_mask = 0x007fffff >> exponent_less_127; if ((w & exponent_mask) == 0) /* x has an integral value. */ return x; w += 0x00400000 >> exponent_less_127; w &= ~exponent_mask; } } else { if (exponent_less_127 == 128) /* x is NaN or infinite. */ return x + x; else return x; } SET_FLOAT_WORD(x, w); return x; }

Por otro lado, si ninguna de las otras soluciones son utilizables, newlib podría ser una opción, ya que es una implementación bien probada.


En estos días no debería ser un problema usar un compilador de C ++ 11 que incluya una biblioteca matemática de C99 / C ++ 11. Pero entonces la pregunta es: ¿qué función de redondeo eliges?

C99 / C ++ 11 round() menudo no es realmente la función de redondeo que desea . Utiliza un modo de redondeo funky que se redondea fuera de 0 como un desempate en casos de mitad de camino ( +-xxx.5000 ). Si desea específicamente ese modo de redondeo, o si está apuntando a una implementación de C ++ donde round() es más rápido que rint() , entonces utilícelo (o emule su comportamiento con una de las otras respuestas en esta pregunta que lo tomó en serio) valor y cuidadosamente reproducido ese comportamiento de redondeo específico.)

El redondeo de round() es diferente del redondeo predeterminado IEEE754 al modo más cercano con incluso como un desempate . Nearest-casi evita el sesgo estadístico en la magnitud promedio de los números, pero hace un sesgo hacia los números pares.

Hay dos funciones de redondeo de la biblioteca matemática que utilizan el modo de redondeo predeterminado actual: std::nearbyint() y std::rint() , ambos agregados en C99 / C ++ 11, por lo que están disponibles en cualquier momento std::round() es. La única diferencia es que nearbyint nunca aumenta FE_INEXACT.

Prefiera rint() por razones de rendimiento : gcc y clang lo incorporan con mayor facilidad, pero gcc nunca alinea nearbyint() (incluso con -ffast-math )

gcc / clang para x86-64 y AArch64

Puse algunas funciones de prueba en el Explorador del compilador de Matt Godbolt , donde puede ver la salida de source + asm (para varios compiladores). Para obtener más información sobre la lectura de la salida del compilador, consulte estas preguntas y respuestas , y la charla de Matt CppCon2017: “¿Qué ha hecho mi compilador por última vez? Desatornillando la tapa del compilador " ,

En el código de PF, por lo general es una gran ganancia para integrar pequeñas funciones. Especialmente en no Windows, donde la convención de llamada estándar no tiene registros de llamadas preservadas, por lo que el compilador no puede mantener ningún valor de FP en los registros XMM a través de una call . Así que incluso si realmente no sabe asm, aún puede ver fácilmente si se trata de una llamada a la función de biblioteca o si está en línea con una o dos instrucciones matemáticas. Cualquier cosa que incluya una o dos instrucciones es mejor que una llamada de función (para esta tarea en particular en x86 o ARM).

En x86, cualquier cosa que esté en línea con SSE4.1 roundsd puede auto-vectorizarse con SSE4.1 roundpd (o AVX vroundpd ). (Las conversiones FP-> integer también están disponibles en formato SIMD empaquetado, excepto para FP-> entero de 64 bits que requiere AVX512).

  • std::nearbyint() :

    • Clan x86: se alinea a una sola entrada con -msse4.1 .
    • x86 gcc: se inserta en una sola inserción solo con -msse4.1 -ffast-math , y solo en gcc 5.4 y anteriores . Más tarde, gcc nunca lo alinea (¿tal vez no se dieron cuenta de que uno de los bits inmediatos puede suprimir la excepción inexacta? Eso es lo que utiliza el argot, pero el gcc más antiguo usa el mismo inmediato que para el rint cuando lo incorpora)
    • AArch64 gcc6.3: se inscribe en una sola entrada de forma predeterminada.
  • std::rint :

    • Clan x86: en línea para una sola inserción con -msse4.1
    • x86 gcc7: en línea para una única inserción con -msse4.1 . (Sin SSE4.1, en línea a varias instrucciones)
    • x86 gcc6.x y versiones anteriores: se alinea a una sola inserción con -ffast-math -msse4.1 .
    • AArch64 gcc: se inscribe en una sola entrada de forma predeterminada
  • std::round :

    • Clan x86: no está en línea
    • x86 gcc: incluye varias instrucciones con -ffast-math -msse4.1 , que requieren dos constantes vectoriales.
    • AArch64 gcc: se alinea con una sola instrucción (el soporte de HW para este modo de redondeo, así como el IEEE predeterminado y la mayoría de los demás).
  • std::floor / std::ceil / std::trunc

    • Clan x86: en línea para una sola inserción con -msse4.1
    • x86 gcc7.x: se alinea a una sola inserción con -msse4.1
    • x86 gcc6.x y versiones anteriores: se alinea a una sola inserción con -ffast-math -msse4.1
    • AArch64 gcc: en línea por defecto a una sola instrucción

Redondeo a int / long / long long :

Tiene dos opciones aquí: use lrint (como rint pero retorna long , o long long para llrint ), o use una función de redondeo FP-> FP y luego convierta a un tipo entero de la manera normal (con truncamiento). Algunos compiladores optimizan una manera mejor que la otra.

long l = lrint(x); int i = (int)rint(x);

Tenga en cuenta que int i = lrint(x) convierte float o double -> long primero, y luego trunca el entero a int . Esto hace una diferencia para los enteros fuera de rango: comportamiento indefinido en C ++, pero bien definido para las instrucciones x86 FP -> int (que el compilador emitirá a menos que vea la UB en tiempo de compilación mientras realiza una propagación constante, entonces es permitido para hacer código que se rompe si alguna vez se ejecuta).

En x86, una conversión de FP-> integer que desborda el entero produce INT_MIN o LLONG_MIN (un patrón de 0x8000000 de 0x8000000 o el equivalente de 64 bits, con solo el signo-bit establecido). Intel llama a esto el valor "entero indefinido". (Consulte la entrada manual de cvttsd2si , la instrucción SSE2 que convierte (con truncamiento) doble escalar en entero con signo. Está disponible con destino de entero de 32 bits o 64 bits (solo en modo de 64 bits). También hay un cvtsd2si (convertir con modo de redondeo actual), que es lo que nos gustaría que emitiera el compilador, pero desafortunadamente gcc y clang no lo harían sin -ffast-math .

También tenga en cuenta que FP a / from unsigned int / long es menos eficiente en x86 (sin AVX512). La conversión a 32 bits sin firma en una máquina de 64 bits es bastante barata; simplemente convertir a 64 bits firmado y truncar. Pero por lo demás es significativamente más lento.

  • -ffast-math -msse4.1 x86 con / sin -ffast-math -msse4.1 : (int/long)rint rint en roundsd a roundsd / cvttsd2si . (optimización perdida a cvtsd2si ). lrint no está en línea en absoluto.

  • x86 gcc6.x y versiones anteriores sin -ffast-math : de ninguna manera en línea

  • x86 gcc7 sin -ffast-math : (int/long)rint redondea y convierte por separado (con 2 instrucciones totales de SSE4.1 está habilitado, de lo contrario con un montón de código en línea para roundsd sin roundsd ). lrint no está en línea.
  • x86 gcc con -ffast-math : todas las formas en línea con cvtsd2si (óptimo) , sin necesidad de SSE4.1.

  • AArch64 gcc6.3 sin -ffast-math : (int/long)rint imprime en 2 instrucciones. lrint no está en línea

  • AArch64 gcc6.3 con -ffast-math : (int/long)rint compila una llamada a lrint . lrint no está en línea. Esta puede ser una optimización perdida a menos que las dos instrucciones que recibimos sin -ffast-math sean muy lentas.

Está disponible desde C ++ 11 en cmath (según http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf )

#include <cmath> #include <iostream> int main(int argc, char** argv) { std::cout << "round(0.5):/t" << round(0.5) << std::endl; std::cout << "round(-0.5):/t" << round(-0.5) << std::endl; std::cout << "round(1.4):/t" << round(1.4) << std::endl; std::cout << "round(-1.4):/t" << round(-1.4) << std::endl; std::cout << "round(1.6):/t" << round(1.6) << std::endl; std::cout << "round(-1.6):/t" << round(-1.6) << std::endl; return 0; }

Salida:

round(0.5): 1 round(-0.5): -1 round(1.4): 1 round(-1.4): -1 round(1.6): 2 round(-1.6): -2


Función double round(double) con el uso de la función modf :

double round(double x) { using namespace std; if ((numeric_limits<double>::max() - 0.5) <= x) return numeric_limits<double>::max(); if ((-1*std::numeric_limits<double>::max() + 0.5) > x) return (-1*std::numeric_limits<double>::max()); double intpart; double fractpart = modf(x, &intpart); if (fractpart >= 0.5) return (intpart + 1); else if (fractpart >= -0.5) return intpart; else return (intpart - 1); }

Para ser compilado limpio, incluye "math.h" y "límites" son necesarios. La función funciona de acuerdo con el siguiente esquema de redondeo:

  • ronda de 5.0 es 5.0
  • ronda de 3.8 es 4.0
  • ronda de 2.3 es 2.0
  • ronda de 1.5 es 2.0
  • ronda de 0.501 es 1.0
  • ronda de 0.5 es 1.0
  • ronda de 0.499 es 0.0
  • ronda de 0.01 es 0.0
  • ronda de 0.0 es 0.0
  • ronda de -0.01 es -0.0
  • ronda de -0.499 es -0.0
  • ronda de -0.5 es -0.0
  • ronda de -0.501 es -1.0
  • ronda de -1.5 es -1.0
  • ronda de -2.3 es -2.0
  • ronda de -3.8 es -4.0
  • ronda de -5.0 es -5.0

Hay 2 problemas que estamos viendo:

  1. conversiones de redondeo
  2. tipo de conversión.

Las conversiones de redondeo significan redondeo ± flotación / doble al piso / ceil flotante / doble más cercano. Puede ser que tu problema termine aquí. Pero si se espera que regrese Int / Long, debe realizar la conversión de tipo y, por lo tanto, el problema de "Desbordamiento" podría afectar su solución. SO, haz una comprobación de error en tu función

long round(double x) { assert(x >= LONG_MIN-0.5); assert(x <= LONG_MAX+0.5); if (x >= 0) return (long) (x+0.5); return (long) (x-0.5); } #define round(x) ((x) < LONG_MIN-0.5 || (x) > LONG_MAX+0.5 ?/ error() : ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))

de: http://www.cs.tut.fi/~jkorpela/round.html


Hice esto:

#include <cmath.h> using namespace std; double roundh(double number, int place){ /* place = decimal point. Putting in 0 will make it round to whole number. putting in 1 will round to the tenths digit. */ number *= 10^place; int istack = (int)floor(number); int out = number-istack; if (out < 0.5){ floor(number); number /= 10^place; return number; } if (out > 0.4) { ceil(number); number /= 10^place; return number; } }


No hay necesidad de implementar nada, así que no estoy seguro de por qué tantas respuestas involucran definiciones, funciones o métodos.

En c99

Tenemos los siguientes yy encabezado <tgmath.h> para macros genéricos de tipo.

#include <math.h> double round (double x); float roundf (float x); long double roundl (long double x);

Si no puede compilar esto, es probable que haya omitido la biblioteca matemática. Un comando similar a esto funciona en cada compilador de C que tengo (varios).

gcc -lm -std=c99 ...

En C ++ 11

Tenemos las siguientes sobrecargas y sobrecargas adicionales en #include <cmath> que se basan en el punto flotante de doble precisión IEEE.

#include <math.h> double round (double x); float round (float x); long double round (long double x); double round (T x);

Hay equivalentes en el espacio de nombres estándar también.

Si no puede compilar esto, puede estar usando la compilación de C en lugar de C ++. El siguiente comando básico no produce errores ni advertencias con g ++ 6.3.1, x86_64-w64-mingw32-g ++ 6.3.0, clang-x86_64 ++ 3.8.0 y Visual C ++ 2015 Community.

g++ -std=c++11 -Wall

Con ordinal division

Cuando se dividen dos números ordinales, donde T es corto, int, largo u otro ordinal, la expresión de redondeo es la siguiente.

T roundedQuotient = (2 * integerNumerator + 1) / (2 * integerDenominator);

Exactitud

No hay duda de que las inexactitudes de aspecto extraño aparecen en las operaciones de punto flotante, pero esto ocurre solo cuando aparecen los números y tiene poco que ver con el redondeo.

La fuente no es solo el número de dígitos significativos en la mantisa de la representación IEEE de un número de punto flotante, sino que está relacionada con nuestro pensamiento decimal como seres humanos.

Diez es el producto de cinco y dos, y 5 y 2 son relativamente primos. Por lo tanto, los estándares de punto flotante IEEE no se pueden representar perfectamente como números decimales para todas las representaciones digitales binarias.

Esto no es un problema con los algoritmos de redondeo. Es una realidad matemática que debe considerarse durante la selección de tipos y el diseño de cálculos, la entrada de datos y la visualización de números. Si una aplicación muestra los dígitos que muestran estos problemas de conversión decimal-binaria, entonces la aplicación está expresando visualmente una precisión que no existe en la realidad digital y debe cambiarse.


No hay round () en la biblioteca estándar de C ++ 98. Aunque puedes escribir uno tú mismo. La siguiente es una implementación de round-half-up :

double round(double d) { return floor(d + 0.5); }

La razón probable de que no haya una función redonda en la biblioteca estándar de C ++ 98 es que, de hecho, se puede implementar de diferentes maneras. Lo anterior es una forma común, pero existen otras como round-to-even , que es menos parcial y generalmente mejor si se va a hacer mucho redondeo; Aunque es un poco más complejo de implementar.


Normalmente se implementa como floor(value + 0.5) .

Edit: y es probable que no se llame round ya que hay al menos tres algoritmos de redondeo que conozco: redondear a cero, redondear al entero más cercano y el redondeo bancario. Estás pidiendo una ronda para el entero más cercano.


Podrías redondear a n dígitos de precisión con:

double round( double x ) { const double sd = 1000; //for accuracy to 3 decimal places return int(x*sd + (x<0? -0.5 : 0.5))/sd; }


Puede que valga la pena tener en cuenta que si desea obtener un resultado entero del redondeo, no necesita pasarlo a través del techo o del piso. Es decir,

int round_int( double r ) { return (r > 0.0) ? (r + 0.5) : (r - 0.5); }


Si finalmente desea convertir la salida double de su función round() en un int , las soluciones aceptadas de esta pregunta serán similares a las siguientes:

int roundint(double r) { return (int)((r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5)); }

Esto se registra en aproximadamente 8.88 ns en mi máquina cuando se pasa en valores uniformemente aleatorios.

El siguiente es funcionalmente equivalente, por lo que puedo decir, pero se registra en 2.48 ns en mi máquina, para una ventaja significativa en el rendimiento:

int roundint (double r) { int tmp = static_cast<int> (r); tmp += (r-tmp>=.5) - (r-tmp<=-.5); return tmp; }

Entre las razones para el mejor rendimiento se encuentra la ramificación omitida.


Un cierto tipo de redondeo también se implementa en Boost:

#include <iostream> #include <boost/numeric/conversion/converter.hpp> template<typename T, typename S> T round2(const S& x) { typedef boost::numeric::conversion_traits<T, S> Traits; typedef boost::numeric::def_overflow_handler OverflowHandler; typedef boost::numeric::RoundEven<typename Traits::source_type> Rounder; typedef boost::numeric::converter<T, S, Traits, OverflowHandler, Rounder> Converter; return Converter::convert(x); } int main() { std::cout << round2<int, double>(0.1) << '' '' << round2<int, double>(-0.1) << '' '' << round2<int, double>(-0.9) << std::endl; }

Tenga en cuenta que esto solo funciona si realiza una conversión a entero.


Uso la siguiente implementación de round in asm para la arquitectura x86 y C ++ específico de MS VS:

__forceinline int Round(const double v) { int r; __asm { FLD v FISTP r FWAIT }; return r; }

UPD: para devolver doble valor.

__forceinline double dround(const double v) { double r; __asm { FLD v FRNDINT FSTP r FWAIT }; return r; }

Salida:

dround(0.1): 0.000000000000000 dround(-0.1): -0.000000000000000 dround(0.9): 1.000000000000000 dround(-0.9): -1.000000000000000 dround(1.1): 1.000000000000000 dround(-1.1): -1.000000000000000 dround(0.49999999999999994): 0.000000000000000 dround(-0.49999999999999994): -0.000000000000000 dround(0.5): 0.000000000000000 dround(-0.5): -0.000000000000000


Basado en la respuesta de Kalaxy, la siguiente es una solución con plantilla que redondea cualquier número de punto flotante al tipo de entero más cercano basado en el redondeo natural. También arroja un error en el modo de depuración si el valor está fuera del rango del tipo entero, por lo que se sirve aproximadamente como una función de biblioteca viable.

// round a floating point number to the nearest integer template <typename Arg> int Round(Arg arg) { #ifndef NDEBUG // check that the argument can be rounded given the return type: if ( (Arg)std::numeric_limits<int>::max() < arg + (Arg) 0.5) || (Arg)std::numeric_limits<int>::lowest() > arg - (Arg) 0.5) ) { throw std::overflow_error("out of bounds"); } #endif return (arg > (Arg) 0.0) ? (int)(r + (Arg) 0.5) : (int)(r - (Arg) 0.5); }


Como se señaló en los comentarios y otras respuestas, la biblioteca estándar ISO C ++ no se agregó round()hasta ISO C ++ 11, cuando esta función se activó por referencia a la biblioteca matemática estándar ISO C99.

Para operandos positivos en [½, ub ] round(x) == floor (x + 0.5), donde ub es 2 23 para floatcuando se asigna a IEEE-754 (2008) binary32, y 2 52 para doublecuando se asigna a IEEE-754 (2008) binary64. Los números 23 y 52 corresponden al número de bits de mantisa almacenados en estos dos formatos de punto flotante. Para operandos positivos en [+0, ½) round(x) == 0, y para operandos positivos en ( ub , + ∞] round(x) == x. Como la función es simétrica respecto al eje x, los argumentos negativos xse pueden manejar de acuerdo con round(-x) == -round(x).

Esto conduce al siguiente código compacto. Se compila en un número razonable de instrucciones de la máquina en varias plataformas. Observé el código más compacto en las GPU, donde se my_roundf()requieren aproximadamente una docena de instrucciones. Dependiendo de la arquitectura del procesador y la cadena de herramientas, este enfoque basado en punto flotante podría ser más rápido o más lento que la implementación basada en enteros de newlib referenciada en una respuesta diferente .

He probado my_roundf()exhaustivamente frente a la newlib roundf()aplicación mediante Intel versión del compilador 13, tanto con /fp:stricty /fp:fast. También verifiqué que la versión de newlib coincide con la roundf()de la mathimfbiblioteca del compilador de Intel. Las pruebas exhaustivas no son posibles para la precisión doble round(), sin embargo, el código es estructuralmente idéntico a la implementación de precisión simple.

#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <math.h> float my_roundf (float x) { const float half = 0.5f; const float one = 2 * half; const float lbound = half; const float ubound = 1L << 23; float a, f, r, s, t; s = (x < 0) ? (-one) : one; a = x * s; t = (a < lbound) ? x : s; f = (a < lbound) ? 0 : floorf (a + half); r = (a > ubound) ? x : (t * f); return r; } double my_round (double x) { const double half = 0.5; const double one = 2 * half; const double lbound = half; const double ubound = 1ULL << 52; double a, f, r, s, t; s = (x < 0) ? (-one) : one; a = x * s; t = (a < lbound) ? x : s; f = (a < lbound) ? 0 : floor (a + half); r = (a > ubound) ? x : (t * f); return r; } uint32_t float_as_uint (float a) { uint32_t r; memcpy (&r, &a, sizeof(r)); return r; } float uint_as_float (uint32_t a) { float r; memcpy (&r, &a, sizeof(r)); return r; } float newlib_roundf (float x) { uint32_t w; int exponent_less_127; w = float_as_uint(x); /* Extract exponent field. */ exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127; if (exponent_less_127 < 23) { if (exponent_less_127 < 0) { /* Extract sign bit. */ w &= 0x80000000; if (exponent_less_127 == -1) { /* Result is +1.0 or -1.0. */ w |= ((uint32_t)127 << 23); } } else { uint32_t exponent_mask = 0x007fffff >> exponent_less_127; if ((w & exponent_mask) == 0) { /* x has an integral value. */ return x; } w += 0x00400000 >> exponent_less_127; w &= ~exponent_mask; } } else { if (exponent_less_127 == 128) { /* x is NaN or infinite so raise FE_INVALID by adding */ return x + x; } else { return x; } } x = uint_as_float (w); return x; } int main (void) { uint32_t argi, resi, refi; float arg, res, ref; argi = 0; do { arg = uint_as_float (argi); ref = newlib_roundf (arg); res = my_roundf (arg); resi = float_as_uint (res); refi = float_as_uint (ref); if (resi != refi) { // check for identical bit pattern printf ("!!!! arg=%08x res=%08x ref=%08x/n", argi, resi, refi); return EXIT_FAILURE; } argi++; } while (argi); return EXIT_SUCCESS; }


Si necesita poder compilar código en entornos que admiten el estándar C ++ 11, pero también debe poder compilar ese mismo código en entornos que no lo admiten, puede usar una macro de funciones para elegir entre std :: round () y una función personalizada para cada sistema. Sólo tiene que pasar -DCPP11o /DCPP11para el compilador de C ++ 11-compatible (o utilizar su base de macros de versión), y hacer una cabecera de la siguiente manera:

// File: rounding.h #include <cmath> #ifdef CPP11 #define ROUND(x) std::round(x) #else /* CPP11 */ inline double myRound(double x) { return (x >= 0.0 ? std::floor(x + 0.5) : std::ceil(x - 0.5)); } #define ROUND(x) myRound(x) #endif /* CPP11 */

Para un ejemplo rápido, vea http://ideone.com/zal709 .

Esto se aproxima a std :: round () en entornos que no son compatibles con C ++ 11, incluida la conservación del bit de signo para -0.0. Sin embargo, puede causar un leve impacto en el rendimiento y es probable que tenga problemas con el redondeo de ciertos valores de punto flotante conocidos como "problema", como 0.49999999999999994 o valores similares.

Alternativamente, si tiene acceso a un compilador compatible con C ++ 11, puede simplemente tomar std :: round () de su <cmath>encabezado, y usarlo para crear su propio encabezado que define la función si aún no está definido. Tenga en cuenta que esta puede no ser una solución óptima, sin embargo, especialmente si necesita compilar para múltiples plataformas.


// Convert the float to a string // We might use stringstream, but it looks like it truncates the float to only //5 decimal points (maybe that''s what you want anyway =P) float MyFloat = 5.11133333311111333; float NewConvertedFloat = 0.0; string FirstString = " "; string SecondString = " "; stringstream ss (stringstream::in | stringstream::out); ss << MyFloat; FirstString = ss.str(); // Take out how ever many decimal places you want // (this is a string it includes the point) SecondString = FirstString.substr(0,5); //whatever precision decimal place you want // Convert it back to a float stringstream(SecondString) >> NewConvertedFloat; cout << NewConvertedFloat; system("pause");

Puede ser una forma sucia e ineficiente de conversión, pero diablos, funciona jajaja. Y es bueno, porque se aplica al flotador real. No solo afectando visualmente la salida.