Pregunta

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

double round(double);

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

Me pueden encontrar ceil() y floor() en las matemáticas.h - pero no round().

Está presente en la biblioteca estándar de C++ bajo otro nombre, o es que faltan??

¿Fue útil?

Solución

No hay round () en la biblioteca estándar de C ++ 98. Sin embargo, 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 por la que no hay 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 hay otras como round-to-even , que es menos parcial y, en general, mejor si va a hacer muchos redondeos; Sin embargo, es un poco más complejo de implementar.

Otros consejos

Boost ofrece un conjunto 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 el Impulso de la documentación.

Editar:Debido a que C++11, hay std::round, std::lround, y std::llround.

El C++03 norma se basa en el C90 estándar para lo que la norma llama a la La Biblioteca Estándar De C que está contemplada en el proyecto de C++03 estándar (más cercano disponible públicamente proyecto de norma para C++03 es N1804 la sección de la 1.2 Referencias normativas:

La biblioteca se describe en la cláusula 7 de la norma ISO/IEC 9899:1990, y la cláusula 7 de ISO/IEC 9899/Amd.1:1995 en adelante llamado el Estándar de C Biblioteca.1)

Si nos vamos a la C documentación para la ronda, lround, llround en cppreference podemos ver que ronda y funciones relacionadas son parte de C99 y por lo tanto no estar disponibles en C++03 o con carácter previo.

En C++11 esto cambia, ya que C++11 se basa en la C99 proyecto de norma para Biblioteca estándar de C y por lo tanto proporciona std::ronda y para el integral de retorno tipo 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 entero más cercano no es de mayor 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 ayuda no C++11 aplicaciones de su mejor apuesta sería la de utilizar impulsar la ronda, iround, lround, llround o impulsar trunc.

Rodar su propia versión de la vuelta es dura

Rodar sus propios probablemente no vale la pena el esfuerzo Más difícil de lo que parece:redondeo flotante a entero más cercano, parte 1, Redondeo flotante a entero más cercano, parte 2 y Redondeo flotante a entero más cercano, parte 3 explicar:

Por ejemplo, un común rollo de su aplicación usando std::floor y la adición de 0.5 no funciona para todas las entradas:

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

Una entrada se producirá un error de es 0.49999999999999994, (ver en directo).

Otra aplicación común consiste en la fundición de un tipo de punto flotante a un tipo integral, que puede invocar un comportamiento indefinido en el caso de que la parte integral no puede ser representado en el tipo de destino.Podemos ver esto desde el proyecto de C++ estándar sección 4.9 Flotante integral de las conversiones que dice (el énfasis es mío):

Un prvalue de un tipo de punto flotante puede ser convertido a un prvalue de un de tipo entero.La conversión trunca;es decir, la parte fraccionaria se descarta. El comportamiento no está definido si el trunca valor no ser representado 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 a continuación, la siguiente llamada:

myround( 4294967296.5f ) 

va a provocar un desbordamiento, (ver en directo).

Podemos ver lo difícil que esto realmente es por mirar esta respuesta a Manera concisa para implementar round() en C? que hacer referencia newlibs la versión de precisión simple flotador redondo.Es muy larga la función de algo que parece simple.Parece poco probable que cualquier persona sin conocimiento íntimo de punto flotante implementaciones podrían 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 potencialmente podría ser una opción, ya que es un bien probado la aplicación.

Vale la pena señalar que si desea un resultado entero del redondeo, no necesita pasarlo por el techo o el piso. Es decir,

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

Está disponible desde C ++ 11 en cmath (de acuerdo con 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

Generalmente se implementa como floor(value + 0.5).

Editar: y probablemente no se llama redondear ya que hay al menos tres algoritmos de redondeo que conozco: redondear a cero, redondear al entero más cercano y redondeo del banquero. Estás pidiendo redondear al entero más cercano.

Hay 2 problemas que estamos viendo:

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

Las conversiones de redondeo significan redondeo & # 177; flotador / doble al piso más cercano / flotador de techo / doble. Puede que tu problema termine aquí. Pero si se espera que devuelva Int / Long, debe realizar la conversión de tipo y, por lo tanto, & Quot; Overflow & Quot; El problema podría afectar a su solución. Entonces, verifique si hay errores en su 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

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 de entero.

Puede redondear a precisión de n dígitos 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;
}

Si finalmente desea convertir la salida double de su función round() en una int, las soluciones aceptadas de esta pregunta se verán así:

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

Esto marca alrededor de 8.88 & nbsp; ns en mi máquina cuando se pasa en valores aleatorios uniformes.

Lo que sigue es funcionalmente equivalente, por lo que puedo decir, pero marca 2.48 & nbsp; ns en mi máquina, para una ventaja de rendimiento significativa:

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

Entre las razones para un mejor rendimiento está la ramificación omitida.

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

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

El redondeo de

std::rint() es diferente del valor predeterminado de IEEE754 redondear al modo más cercano incluso con un desempate . El más cercano, incluso evita el sesgo estadístico en la magnitud promedio de los números, pero lo hace hacia los números pares.

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

Prefiero call por razones de rendimiento : gcc y clang lo alinean más fácilmente, pero gcc nunca se alinea roundsd (incluso con roundpd)


gcc / clang para x86-64 y AArch64

Puse algunas funciones de prueba en el Compilador Explorer de Matt Godbolt , donde puede ver la salida de fuente + asm (para múltiples compiladores) . Para obtener más información sobre la lectura del resultado del compilador, consulte this Q & amp; A , y Matt's CppCon2017 talk: & # 8220; ¿Qué ha hecho mi compilador últimamente por mí? Desatornillar la tapa del compilador & # 8221; ,

En el código FP, generalmente es una gran victoria para las pequeñas funciones en línea. Especialmente en no Windows, donde la convención de llamadas 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 un vroundpd. Entonces, incluso si realmente no conoce asm, aún puede ver fácilmente si es solo una llamada a la función de la biblioteca o si está alineado con una o dos instrucciones matemáticas. Cualquier cosa que se alinee con una o dos instrucciones es mejor que una llamada de función (para esta tarea en particular en x86 o ARM).

En x86, todo lo que esté en línea con SSE4.1 -msse4.1 puede auto-vectorizarse con SSE4.1 -msse4.1 -ffast-math (o AVX rint). (FP - & Gt; las conversiones de enteros también están disponibles en forma SIMD empaquetada, excepto FP - & Gt; entero de 64 bits que requiere AVX512.)

  • std::rint:

    • x86 clang: en línea a una única insn con -ffast-math -msse4.1.
    • x86 gcc: se alinea a una única insn solo con std::round, y solo en gcc 5.4 y anterior . 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 usa el sonido clang, pero el gcc más antiguo usa el mismo inmediato que para std::floor cuando lo hace en línea)
    • AArch64 gcc6.3: en línea a un solo insn por defecto.
  • std::ceil:

    • x86 clang: en línea a un solo insn con std::trunc
    • x86 gcc7: se alinea en un solo insn con int. (Sin SSE4.1, en línea con varias instrucciones)
    • x86 gcc6.xy versiones anteriores: se alinea en un solo insn con long.
    • AArch64 gcc: en línea a un solo insn por defecto
  • long long:

    • x86 clang: no está en línea
    • x86 gcc: en línea con múltiples instrucciones con lrint, que requieren dos constantes vectoriales.
    • AArch64 gcc: en línea a una sola instrucción (soporte HW para este modo de redondeo, así como el predeterminado IEEE y la mayoría de los demás).
  • llrint / int i = lrint(x) / float

    • x86 clang: en línea a un solo insn con double
    • x86 gcc7.x: se alinea en un solo insn con INT_MIN
    • x86 gcc6.xy versiones anteriores: en línea a una única insn con LLONG_MIN
    • AArch64 gcc: en línea por defecto a una sola instrucción

Redondeando a 0x8000000 / cvttsd2si / cvtsd2si:

Tiene dos opciones aquí: use unsigned (como (int/long)rint pero devuelve <=>, o <=> para <=>), o use una función de redondeo FP - > FP y luego convierta a un número entero de la forma normal (con truncamiento). Algunos compiladores se optimizan de una manera mejor que la otra.

long l = lrint(x);

int  i = (int)rint(x);

Tenga en cuenta que <=> convierte <=> o <=> - > <=> primero, y luego trunca el entero a <=>. Esto hace una diferencia para los enteros fuera de rango: Comportamiento indefinido en C ++, pero bien definido para x86 FP - & Gt; instrucciones int (que emitirá el compilador a menos que vea el UB en tiempo de compilación mientras realiza una propagación constante, entonces se le permite crear código que se rompa si alguna vez se ejecuta).

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

También ten en cuenta que FP a / desde <=> int / long es menos eficiente en x86 (sin AVX512). La conversión a 32 bits sin firmar en una máquina de 64 bits es bastante barata; simplemente convierta a 64 bits con signo y truncar. Pero por lo demás es significativamente más lento.

  • x86 clang con / sin <=>: <=> en línea a <=> / <=>. (se perdió la optimización para <=>). <=> no está en línea en absoluto.

  • x86 gcc6.xy versiones anteriores sin <=>: ninguna de las dos líneas

  • x86 gcc7 sin <=>: <=> 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 <=> sin <=>). <=> no está en línea.
  • x86 gcc con <=>: en línea a <=> (óptimo) , sin necesidad de SSE4.1.

  • AArch64 gcc6.3 sin <=>: <=> en línea a 2 instrucciones. <=> no está en línea

  • AArch64 gcc6.3 con <=>: <=> compila a una llamada a <=>. <=> no está en línea. Esta puede ser una optimización perdida a menos que las dos instrucciones que recibimos sin <=> sean muy lentas.

Cuidado con floor(x+0.5). Esto es lo que puede suceder para 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 . Use una solución como la de @konik.

Mi propia versión robusta sería algo así 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 aquí .

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 lo siguiente y el encabezado < tgmath.h > para macros de tipo genérico.

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

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

gcc -lm -std=c99 ...

En C ++ 11

Tenemos las siguientes sobrecargas adicionales en #include < cmath > que confían en el punto flotante de precisión doble 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 .

Si no puede compilar esto, puede estar utilizando la compilación 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 División Ordinal

Al dividir dos números ordinales, donde T es corto, int, largo u otro ordinal, la expresión de redondeo es esta.

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

Precisión

No hay duda de que aparecen inexactitudes de aspecto extraño en las operaciones de coma flotante, pero esto es 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 coma flotante, está relacionado con nuestro pensamiento decimal como humanos.

Diez es el producto de cinco y dos, y 5 y 2 son relativamente primos. Por lo tanto, los estándares de coma flotante IEEE no pueden representarse 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, entrada de datos y visualización de números. Si una aplicación muestra los dígitos que muestran estos problemas de conversión decimal-binario, entonces la aplicación está expresando visualmente una precisión que no existe en la realidad digital y debe cambiarse.

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

Si usted necesita para ser capaz de compilar código en entornos en los que se soporte el estándar C++11, pero también necesita ser capaz de compilar el mismo código en entornos en los que no lo apoyan, puede utilizar una función macro para elegir entre std::round() y una función personalizada para cada sistema.Acaba de pasar -DCPP11 o /DCPP11 para el C++11-compatible con el compilador (o utilice su versión integrada de macros), y hacer un encabezado como este:

// 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 rápido ejemplo, ver http://ideone.com/zal709 .

Esto se aproxima a std::round() en entornos que no son de C++11-compatible, incluyendo la preservación del bit de signo para -0.0.Puede causar una ligera pérdida de rendimiento, sin embargo, y es probable que tenga problemas con el redondeo de ciertos conocido "problema" de los valores de punto flotante como 0.49999999999999994 o valores similares.

Alternativamente, si usted tiene acceso a una C++11-compatible con el compilador, sólo podía agarrar std::round() de su <cmath> encabezado, y lo utilizan para hacer su propio encabezado que define la función, si es que aún no está definido.Tenga en cuenta que esto no puede ser una solución óptima, sin embargo, especialmente si usted necesita para compilar para múltiples plataformas.

Basado en la respuesta de Kalaxy, la siguiente es una solución con plantilla que redondea cualquier número de coma flotante al tipo entero más cercano en función del redondeo natural. También arroja un error en modo de depuración si el valor está fuera del rango del tipo entero, lo que 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 en otras respuestas, el ISO estándar de C++ biblioteca no agregar round() hasta ISO C++11, cuando esta función se tiraba en la referencia a la norma ISO estándar C99 de matemáticas de la biblioteca.

Positivas de los operandos [½, ub] round(x) == floor (x + 0.5), donde ub es 223 para float cuando se asigna a la IEEE-754 (2008) binary32, y 252 para double cuando se asigna a IEEE-754 (2008) binary64.Los números 23 y 52 corresponden al número de almacenado bits de mantisa en estos dos punto flotante de formatos.Positivas de los operandos [+0, ½) round(x) == 0, y positivas de los operandos (ub, +∞] round(x) == x.Como la función es simétrica sobre el eje x, argumentos negativos x puede ser manejado de acuerdo a las round(-x) == -round(x).

Esto conduce a la compacta código de abajo.Se compila en un número razonable de instrucciones de la máquina a través de diversas plataformas.He observado que la mayoría de los compactos código de la Gpu, donde my_roundf() se requiere alrededor de una docena de instrucciones.Dependiendo de la arquitectura del procesador y de las herramientas, en este punto flotante basado en el enfoque podría ser más rápido o más lento que el entero basado en la aplicación de newlib que se hace referencia en un respuesta diferente.

He probado my_roundf() exhaustivamente en contra de la newlib roundf() utilizando la implementación de Intel compiler versión 13, con tanto /fp:strict y /fp:fast.También comprobé que la newlib versión coincide con la roundf() en el mathimf biblioteca de el compilador de Intel.Exhaustivas pruebas no es posible para doble precisión round(), sin embargo , el código es estructuralmente idéntica a la de precisión simple aplicación.

#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;
}

Utilizo 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 el valor doble

__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

La mejor manera de redondear un valor flotante por " n " lugares decimales, es como sigue con en O (1) tiempo: -

Tenemos que redondear el valor en 3 lugares, es decir, n = 3. Entonces,

float a=47.8732355;
printf("%.3f",a);
// 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 manera sucia ineficiente de conversión, pero diablos, funciona jajaja. Y es bueno, porque se aplica al flotador real. No solo afecta la salida visualmente.

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;
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top