Pergunta

Eu preciso de uma simples função de arredondamento de ponto flutuante, assim:

double round(double);

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

posso encontrar ceil() e floor() na math.h - mas não round()

.

É apresentar na biblioteca padrão C ++ sob outro nome, ou é falta ??

Foi útil?

Solução

Não há nenhum round () no C ++ 98 biblioteca padrão. Você pode escrever um você mesmo embora. O seguinte é uma implementação do round-meia-se :

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

A provável razão não há nenhuma função redonda no C ++ 98 biblioteca padrão é que ele pode de fato ser implementado de maneiras diferentes. A descrição acima é uma maneira comum, mas existem outros, como round-a-even , que é menos tendencioso e geralmente melhor se você estiver indo para fazer um monte de arredondamento; é um pouco mais complexo de implementar embora.

Outras dicas

impulso oferece um conjunto simples de arredondamento funções.

#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 obter mais informações, consulte o documentação impulso .

Editar : Desde C ++ 11, há std::round href="http://en.cppreference.com/w/cpp/numeric/math/round">, std::lround, e std::llround .

O padrão C ++ 03 se baseia no padrão C90 para o que as chamadas padrão do Padrão C Library que é coberta no projecto C ++ 03 standard ( mais próxima disponível publicamente projecto de norma para C ++ 03 é N1804 ) seção 1.2 referências normativas :

A biblioteca descrito na cláusula 7 da norma ISO / IEC 9899: 1990 cláusula e 7 de ISO / IEC 9899 / Amd.1: 1995 é daqui em diante chamado o padrão C Biblioteca. 1)

Se formos para o documentação C para a segunda rodada, lround, llround em cppreference podemos ver que volta e funções relacionadas são parte da C99 e, portanto, não estará disponível em C ++ 03 ou antes.

Em C ++ 11 este mudanças desde C ++ 11 depende do projecto de norma C99 para C biblioteca padrão e, portanto, fornece std :: rodada e para tipos de retorno integrais std ::, 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 ;
}

Outra opção também de C99 seria std :: trunc que :

Calcula mais próximo inteiro não maior em magnitude do 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 ;

}

Se você precisa para apoiar non C ++ 11 aplicações a sua melhor aposta seria a utilização de boost rodada, iround, lround, llround ou boost trunc .

Rolando sua própria versão da rodada é difícil

Rolling seu próprio é provavelmente não vale a pena o esforço como mais difícil do que parece: arredondamento float para número inteiro mais próximo, parte 1 , Completando float para número inteiro mais próximo, parte 2 e Completando float para número inteiro mais próximo, parte 3 explicar:

Por exemplo, um rolo comum sua implementação usando std::floor e adicionando 0.5 não funciona para todas as entradas:

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

Uma entrada esta falhará para é 0.49999999999999994, ( vê-lo ao vivo ).

Outra aplicação comum envolve lançando um tipo de ponto flutuante para um tipo integral, que pode invocar um comportamento indefinido no caso em que a parte integral não pode ser representado no tipo de destino. Podemos ver isso a partir do projecto de C ++ 4.9 seção padrão Floating-integrais conversões que diz ( grifo meu ):

Um prvalue de um tipo de ponto flutuante pode ser convertido para um prvalue de um tipo inteiro. Os truncados de conversão; isto é, a parte fracionária é descartado. O comportamento é indefinido se o valor truncado não pode ser representado no tipo de destino. [...]

Por exemplo:

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

std::numeric_limits<unsigned int>::max() Dado é 4294967295 em seguida, a seguinte chamada:

myround( 4294967296.5f ) 

irá causar excesso, ( vê-lo viver ) .

Podemos ver quão difícil isso realmente é, olhando para esta resposta para Concise maneira de implementar round () em C? que fazem referência a newlibs versão de precisão simples bóia redonda. É um tempo muito longo função de algo que parece simples. Parece improvável que qualquer pessoa sem conhecimento íntimo de implementações de ponto flutuante poderia implementar corretamente essa função:

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 outro lado, se nenhuma das outras soluções são utilizáveis ?? newlib poderia ser uma opção, uma vez que é uma implementação bem testado.

Pode valer a pena notar que, se você queria um resultado inteiro do arredondamento não é necessário passá-lo através de qualquer ceil ou no chão. I.,

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

Está disponível desde C ++ 11 em cmath (de acordo com 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;
}

Output:

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

Geralmente é implementado como floor(value + 0.5).

Edit: e é, provavelmente, não é chamado rodada uma vez que existem pelo menos três algoritmos de arredondamento que eu conheço: rodada para zero, volta para número inteiro mais próximo, e arredondamento do banqueiro. Você está pedindo rodada para número inteiro mais próximo.

Existem 2 problemas que estamos olhando:

  1. arredondamento conversões
  2. conversão de tipo.

conversões arredondamento dizer arredondamento ± float / double ao piso mais próximo / float ceil / double. Podem ser seus fins problema aqui. Mas se você é esperado para retornar Int / Long, você precisa executar conversão de tipo e, portanto, problema "Estouro" pode acertar a sua solução. Então, faça um cheque de erro em sua função

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

Um certo tipo de arredondamento também é implementado em 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;
}

Note que isto só funciona se você fizer uma conversão para inteiro.

Você poderia rodada de n precisão dígitos com:

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

Se você quer finalmente para converter a saída double da sua função round() a um int, então as soluções aceitas de esta questão será algo parecido com:

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

Este relógios em torno de 8.88 ns na minha máquina quando passado em valores uniformemente aleatórios.

A seguir é funcionalmente equivalente, tanto quanto eu posso dizer, mas relógios em 2.48 ns na minha máquina, para uma vantagem de desempenho significativa:

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

Entre as razões para o melhor desempenho é o ignorada ramificação.

Estes dias não deve ser um problema para usar um C ++ 11 compilador que inclui uma biblioteca / C ++ 11 de matemática C99. Mas, então, a pergunta é: qual o arredondamento função você escolhe

C99 / C ++ 11 round() muitas vezes não é realmente a função de arredondamento você quiser . Ele usa um modo de funk de arredondamento que rodadas de distância de 0 como um tie-break em casos half-way (+-xxx.5000). Se você quer especificamente que o modo de arredondamento, ou você está alvejando uma implementação C ++ onde round() é mais rápido que rint(), em seguida, usá-lo (ou emular seu comportamento com uma das outras respostas sobre esta questão que tomaram pelo valor de face e cuidadosamente reproduzidas que específica arredondamento comportamento.)

arredondamento round() é diferente do IEEE754 padrão rodada para o modo mais próximo com mesmo como um tie-break . Mais próximo, até mesmo evita viés estatístico na magnitude média de números, mas faz viés para números pares.

Existem duas funções biblioteca de matemática de arredondamento que usam o padrão atual modo de arredondamento: std::nearbyint() e std::rint() , ambos adicionados em C99 / C ++ 11, para que eles estejam disponíveis a qualquer std::round() tempo é. A única diferença é que nearbyint não levanta FE_INEXACT.

Prefere rint() por motivos de desempenho : gcc e clang tanto linha-lo mais facilmente, mas gcc não inlines nearbyint() (mesmo com -ffast-math)


gcc / clang para x86-64 e AArch64

Eu coloquei alguns funções de teste em Matt Godbolt Compiler Explorer , onde você pode ver fonte + saída asm (para vários compiladores). Para mais sobre a leitura de saída do compilador, consulte este Q & A e de Matt CppCon2017 Discussão: “O que tem a minha Compiler feito por mim ultimamente? Unbolting tampa do Compiler”,

No código FP, geralmente é uma grande vitória para inline pequenas funções. Especialmente sobre a não-Windows, onde a convenção de chamada padrão não tem registos preservados-chamada, de modo que o compilador não pode manter todos os valores PF em registros XMM através de uma call. Assim, mesmo se você realmente não sei asm, você ainda pode ver facilmente se é apenas um rabo-chamada para a função de biblioteca ou se inlined a uma ou duas instruções matemáticas. Qualquer coisa que inlines para uma ou duas instruções é melhor do que uma chamada de função (para esta tarefa particular em x86 ou ARM).

Em x86, qualquer coisa que inlines para SSE4.1 roundsd pode auto-vectorize com SSE4.1 roundpd (ou vroundpd AVX). (FP-> conversões inteiros também estão disponíveis na forma SIMD embalado, excepto para FP-> inteiro de 64-bit que requer AVX512.)

  • std::nearbyint():

    • x86 clang:. Inlines para um único insn com -msse4.1
    • x86 gcc: inlines para um único insn apenas com -msse4.1 -ffast-math, e só no gcc 5.4 e anterior . Mais tarde gcc não inlines (talvez eles não perceberam que um dos bits imediatos podem suprimir a exceção inexata? Isso é o que os usos clang, mas mais velhos gcc usa o mesmo imediata, para rint quando o faz em linha-lo)
    • AArch64 gcc6.3:. Inlines para um único insn por padrão
  • std::rint:

    • x86 clang: inlines para um único insn com -msse4.1
    • gcc7 x86: inlines para um único insn com -msse4.1. (Sem SSE4.1, inlines para várias instruções)
    • x86 gcc6.x e anterior:. Inlines para um único insn com -ffast-math -msse4.1
    • AArch64 gcc: inlines para um único insn por padrão
  • std::round:

    • x86 clang: não em linha
    • x86 gcc:. Inlines para várias instruções com -ffast-math -msse4.1, exigindo duas constantes vetor
    • AArch64 gcc: (. Suporte HW para este modo de arredondamento, bem como padrão IEEE e mais outros) inlines para uma única instrução
  • std::floor / std::ceil / std::trunc

    • x86 clang: inlines para um único insn com -msse4.1
    • x86 gcc7.x: inlines para um único insn com -msse4.1
    • x86 gcc6.x e anterior: inlines para um único insn com -ffast-math -msse4.1
    • AArch64 gcc: inlines por padrão para uma única instrução

arredondamento para int / long / long long:

Você tem duas opções aqui: uso lrint (como rint mas retorna long, ou long long para llrint), ou usar um FP-> FP função de arredondamento e depois converter para um inteiro digite o caminho normal (com truncagem). Alguns compiladores otimizar uma maneira melhor do que o outro.

long l = lrint(x);

int  i = (int)rint(x);

Note que converte int i = lrint(x) float ou double -> long primeiro, e depois trunca o inteiro para int. Isso faz uma diferença para inteiros out-of-range: comportamento indefinido em C ++, mas bem definido para o x86 FP -> instruções int (que o compilador irá emitir menos que vê a UB em tempo de compilação ao fazer propagação constante, então é permissão para fazer código que quebra se alguma vez executado).

Em x86, um FP-> intEger conversão que transborda o número inteiro produz INT_MIN ou LLONG_MIN (um bit-padrão de 0x8000000 ou o 64-bit equivalente, com apenas o conjunto sinal bits). Intel chama este o valor "inteiro indefinido". (Veja o cvttsd2si entrada manual, a instrução SSE2 que converte (com truncagem) escalar dupla para assinado inteiro. Ele está disponível com 32-bit ou destino inteiro de 64 bits (no modo de 64 bits apenas). Há também uma cvtsd2si (converso com o modo de arredondamento atual), que é o que nós gostaríamos que o compilador para emitir, mas infelizmente gcc e clang não vai fazer isso sem -ffast-math.

Também cuidado com que FP de / para int unsigned / longo é menos eficiente em x86 (sem AVX512). A conversão para 32 bits sem sinal em uma máquina de 64 bits é muito barato; apenas converter a 64-bit assinado e truncado. Mas caso contrário, é significativamente mais lento.

  • x86 clang com / sem -ffast-math -msse4.1: (int/long)rint inlines para roundsd / cvttsd2si. (Perdeu otimização para cvtsd2si). lrint não embutido em tudo.

  • x86 gcc6.x e anteriores sem -ffast-math: nem maneira inlines

  • gcc7 x 86 sem -ffast-math: rodadas (int/long)rint e convertidos separadamente (com 2 instruções totais de SSE4.1 é activado, de outra forma com um grupo de código embutido para rint sem roundsd). lrint não em linha.
  • x86 gcc com -ffast-math:. todas as formas inline para cvtsd2si (ideal) , não há necessidade de SSE4.1

  • AArch64 gcc6.3 sem -ffast-math: inlines (int/long)rint 2 instruções. lrint não em linha

  • gcc6.3 AArch64 com -ffast-math: compila (int/long)rint para uma chamada para lrint. lrint não inline. Esta pode ser uma otimização perdeu a menos que as duas instruções que recebemos sem -ffast-math são muito lento.

Cuidado com floor(x+0.5). Aqui está o que pode acontecer por números ímpares no intervalo [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

Esta é http://bugs.squeak.org/view.php?id = 7134 . Use uma solução como a de @konik.

A minha própria versão robusta seria algo como:

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

Outra razão para evitar andar (x + 0,5) é dada aqui .

Não há necessidade de implementar qualquer coisa, então eu não tenho certeza por que tantas respostas envolvem define, funções ou métodos.

Em C99

Nós temos o seguinte e e cabeçalho para macros de tipo genérico.

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

Se você não pode compilar este, você provavelmente já deixou de fora a biblioteca de matemática. Um comando semelhante a este obras em cada compilador C Eu tenho (vários).

gcc -lm -std=c99 ...

Em C ++ 11

Temos as seguintes e adicionais sobrecargas em #include que dependem de IEEE de ponto flutuante de precisão dupla.

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

equivalentes no std namespace também.

Se você não pode compilar isso, você pode estar usando compilação C em vez de C ++. O seguinte comando básica produz nem erros nem avisos com g ++ 6.3.1, x86_64-w64-mingw32-g ++ 6.3.0, clang-x86_64 ++ 3.8.0, e Visual C ++ 2015 comunitário.

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

Com ordinal Divisão

Ao dividir dois números ordinais, onde T é curto, int, long ou outro ordinais, a expressão arredondamento é isso.

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

Precisão

Não há dúvida de que estranhos imprecisões procurando aparecer em operações de ponto flutuante, mas isso é apenas quando os números aparecem, e tem pouco a ver com arredondamento.

A fonte não é apenas o número de dígitos significativos no mantissa da representação IEEE de um número de ponto flutuante, que está relacionado com o nosso pensamento decimal como seres humanos.

Ten é o produto de cinco e dois, e 5 e 2 são relativamente primos. Portanto, os padrões IEEE ponto flutuante não pode ser representado perfeitamente como números decimais para todas as representações digitais binários.

Este não é um problema com os algoritmos de arredondamento. É a realidade matemática que deve ser considerado durante a seleção de tipos e o projeto de cálculos, entrada de dados e exibição de números. Se um aplicativo exibe os dígitos que mostram esses problemas de conversão decimais-binário, em seguida, o aplicativo é visualmente expressar precisão que não existe na realidade digital e deve ser alterado.

double round(double) função com o uso da função 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 compilação limpa, inclui "math.h" e "limites" são necessárias. A função funciona de acordo com um esquema de arredondamento seguinte:

  • rodada de 5.0 é de 5,0
  • rodada de 3.8 é 4.0
  • rodada de 2.3 é de 2,0
  • rodada de 1,5 é de 2,0
  • rodada de 0,501 é 1,0
  • rodada de 0,5 é de 1,0
  • rodada de 0,499 é 0,0
  • rodada de 0,01 é de 0,0
  • rodada de 0.0 é de 0,0
  • rodada de -0.01 é -0.0
  • rodada de -0,499 é -0.0
  • rodada de -0.5 é -0.0
  • rodada de -0,501 é -1.0
  • rodada de -1.5 é -1.0
  • rodada de -2.3 é -2.0
  • rodada de -3.8 é -4.0
  • rodada de -5.0 é -5.0

Se você precisa ser capaz de compilar o código em ambientes que suportam o C ++ 11 standard, mas também precisa ser capaz de compilar esse mesmo código em ambientes que não apoiá-lo, você pode usar uma macro função para escolher entre std :: round () e uma função personalizada para cada sistema. Basta passar -DCPP11 ou /DCPP11 para o compilador C ++ 11-compliant (ou use a sua built-in macros versão), e fazer um cabeçalho 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 um exemplo rápido, consulte http://ideone.com/zal709 .

Isto aproxima std :: round () em ambientes que não são C ++ 11-compliant, incluindo a preservação do bit de sinal para -0,0. Isso pode causar uma pequena queda de performance, no entanto, e provavelmente vai ter problemas com arredondamento certo "problema" conhecido valores de ponto flutuante, como 0.49999999999999994 ou valores semelhantes.

Como alternativa, se você tiver acesso a um compilador 11-compliant C ++, você pode simplesmente pegar std :: round () a partir de seu cabeçalho <cmath>, e usá-lo para fazer o seu próprio cabeçalho que define a função, se ainda não estiver definiram. Note que isso pode não ser a solução ideal, no entanto, especialmente se você precisa compilar para múltiplas plataformas.

Com base na resposta de Kalaxy, o seguinte é uma solução que modelada rodadas qualquer número de ponto flutuante para o número inteiro mais próximo tipo baseado em arredondamento natural. Ele também gera um erro no modo de depuração se o valor está fora do intervalo do tipo inteiro, servindo aproximadamente como uma função da biblioteca viável.

    // 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 apontado em comentários e outras respostas, biblioteca padrão ISO C ++ não adicionar round() até ISO C ++ 11, quando esta função foi puxado por referência à biblioteca de matemática padrão ISO C99.

Para operandos positivos em [½, ub ] round(x) == floor (x + 0.5), onde ub é 2 23 para float quando mapeado para IEEE-754 (2008 ) binary32 e 2 52 para double quando é mapeado para-754 IEEE (2008) binary64. Os números 23 e 52 correspondem ao número de armazenado bits de mantissa nestes dois formatos de ponto flutuante. Para operandos positivos em [0, ½) round(x) == 0, e para operandos positivas na ( ub , + 8] round(x) == x. À medida que a função é simétrica em torno do eixo-x, argumentos negativos x podem ser tratadas de acordo para round(-x) == -round(x).

Isso leva ao código compacto abaixo. Ele compila em um número razoável de instruções de máquina em várias plataformas. Observei o código mais compacto em GPUs, onde my_roundf() requer cerca de uma dúzia de instruções. Dependendo da arquitectura do processador e conjunto de ferramentas, esta abordagem baseia-ponto flutuante pode ser mais rápido ou mais lento do que a implementação baseada no número inteiro de newlib referenciados em um diferente resposta .

Eu testei my_roundf() exaustivamente contra a implementação roundf() newlib usando Intel compilador versão 13, com ambos /fp:strict e /fp:fast. Eu também verificado que a versão newlib corresponde ao roundf() na biblioteca mathimf do compilador Intel. testes exaustivos não é possível para round() de precisão dupla, no entanto, o código é estruturalmente idêntica à implementação precisão simples.

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

Eu uso o seguinte implementação da rodada em asm para a arquitetura x86 e MS VS específica C ++:

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

UPD: para retornar o valor double

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

Output:

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

A melhor maneira de arredondamento um valor flutuante por "n" casas decimais, é o seguinte com em O (1) tempo: -

Temos que completam o valor por 3 lugares ou seja n = 3.So,

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");

Pode ser uma maneira suja ineficiente de conversão, mas que diabo, ele funciona lol. E é bom, porque se aplica à bóia real. Não apenas afetar a saída visual.

Eu fiz isso:

#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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top