Como arredondar um valor flutuante (como 37,777779) para duas casas decimais (37,78) em C?
Como arredondar um valor flutuante (como 37,777779) para duas casas decimais (37,78) em C?
Respostas:
Se você deseja arredondar o número apenas para fins de saída, a "%.2f"
sequência de formatação é realmente a resposta correta. No entanto, se você realmente deseja arredondar o valor do ponto flutuante para cálculos adicionais, algo como o seguinte funciona:
#include <math.h>
float val = 37.777779;
float rounded_down = floorf(val * 100) / 100; /* Result: 37.77 */
float nearest = roundf(val * 100) / 100; /* Result: 37.78 */
float rounded_up = ceilf(val * 100) / 100; /* Result: 37.78 */
Observe que você pode escolher três regras de arredondamento diferentes: arredondar para baixo (ou seja, truncar após duas casas decimais), arredondar para o mais próximo e arredondar para cima. Normalmente, você deseja arredondar para o mais próximo.
Como vários outros apontaram, devido às peculiaridades da representação de ponto flutuante, esses valores arredondados podem não ser exatamente os valores decimais "óbvios", mas estarão muito próximos.
Para muito (muito!) Mais informações sobre arredondamento, e especialmente sobre regras de desempate para arredondar para o mais próximo, consulte o artigo da Wikipedia sobre Arredondamento .
doubles
alguma maneira? Parece não fazer o trabalho que eu quero :( (usando floor
e ceil
). #
Usando % .2f em printf. Imprime apenas 2 casas decimais.
Exemplo:
printf("%.2f", 37.777779);
Resultado:
37.77
float
alcance, pois val * 100
poderia estourar.
Supondo que você esteja falando sobre o valor da impressão, as respostas de Andrew Coleson e AraK estão corretas:
printf("%.2f", 37.777779);
Mas observe que, se você deseja arredondar o número para exatamente 37,78 para uso interno (por exemplo, comparar com outro valor), isso não é uma boa ideia, devido à maneira como os números de ponto flutuante funcionam: você normalmente não deseja fazer comparações de igualdade para ponto flutuante; em vez disso, use um valor-alvo +/- um valor sigma. Ou codifique o número como uma sequência com uma precisão conhecida e compare-a.
Veja o link na resposta de Greg Hewgill para uma pergunta relacionada , que também cobre por que você não deve usar ponto flutuante para cálculos financeiros.
printf("%.*f", (int)precision, (double)number);
Que tal agora:
float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);
printf("%.2f", 37.777779);
Se você deseja gravar na string C:
char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
Não existe uma maneira de arredondar de um float
para outro float
porque o arredondado float
pode não ser representável (uma limitação dos números de ponto flutuante). Por exemplo, digamos que você arredonde 37.777779 para 37.78, mas o número representável mais próximo é 37.781.
No entanto, você pode "arredondar" a float
usando uma função de seqüência de caracteres de formato.
float
a n casas decimais e esperar que o resultado sempre tenha n casas decimais. Você ainda receberá um float
, mas não o que você esperava.
Além disso, se você estiver usando C ++, você pode apenas criar uma função como esta:
string prd(const double x, const int decDigits) {
stringstream ss;
ss << fixed;
ss.precision(decDigits); // set # places after decimal
ss << x;
return ss.str();
}
Em seguida, você pode emitir qualquer duplo myDouble
com n
casas após o ponto decimal com código como este:
std::cout << prd(myDouble,n);
Você ainda pode usar:
float ceilf(float x); // don't forget #include <math.h> and link with -lm.
exemplo:
float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;
No C ++ (ou no C com conversão no estilo C), você pode criar a função:
/* Function to control # of decimal places to be output for x */
double showDecimals(const double& x, const int& numDecimals) {
int y=x;
double z=x-y;
double m=pow(10,numDecimals);
double q=z*m;
double r=round(q);
return static_cast<double>(y)+(1.0/m)*r;
}
Então std::cout << showDecimals(37.777779,2);
produziria: 37.78.
Obviamente, você realmente não precisa criar todas as 5 variáveis nessa função, mas eu as deixo lá para que você possa ver a lógica. Provavelmente existem soluções mais simples, mas isso funciona bem para mim - especialmente porque me permite ajustar o número de dígitos após a casa decimal conforme necessário.
Sempre use a printf
família de funções para isso. Mesmo se você quiser obter o valor como flutuante, é melhor usá-lo snprintf
para obter o valor arredondado como uma sequência e analisá-lo novamente com atof
:
#include <math.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
double dround(double val, int dp) {
int charsNeeded = 1 + snprintf(NULL, 0, "%.*f", dp, val);
char *buffer = malloc(charsNeeded);
snprintf(buffer, charsNeeded, "%.*f", dp, val);
double result = atof(buffer);
free(buffer);
return result;
}
Digo isso porque a abordagem mostrada pela resposta atualmente mais votada e várias outras aqui - multiplicando por 100, arredondando para o número inteiro mais próximo e depois dividindo por 100 novamente - é falha de duas maneiras:
Para ilustrar o primeiro tipo de erro - a direção do arredondamento às vezes está errada - tente executar este programa:
int main(void) {
// This number is EXACTLY representable as a double
double x = 0.01499999999999999944488848768742172978818416595458984375;
printf("x: %.50f\n", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.50f\n", res1);
printf("Rounded with round, then divided: %.50f\n", res2);
}
Você verá esta saída:
x: 0.01499999999999999944488848768742172978818416595459
Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406
Observe que o valor com o qual começamos era menor que 0,015 e, portanto, a resposta matematicamente correta ao arredondá-lo para 2 casas decimais é 0,01. Obviamente, 0,01 não é exatamente representável como um dobro, mas esperamos que nosso resultado seja o dobro mais próximo de 0,01. Usar snprintf
nos dá esse resultado, mas usar round(100 * x) / 100
nos dá 0,02, o que está errado. Por quê? Porque 100 * x
nos dá exatamente 1,5 como resultado. Multiplicar por 100 altera a direção correta para arredondar.
Para ilustrar o segundo tipo de erro - o resultado, às vezes, é errado devido * 100
e / 100
não é realmente um inverso um do outro - podemos fazer um exercício semelhante com um número muito grande:
int main(void) {
double x = 8631192423766613.0;
printf("x: %.1f\n", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.1f\n", res1);
printf("Rounded with round, then divided: %.1f\n", res2);
}
Nosso número agora nem tem uma parte fracionária; é um valor inteiro, apenas armazenados com o tipo double
. Então o resultado após o arredondamento deve ser o mesmo número com o qual começamos, certo?
Se você executar o programa acima, verá:
x: 8631192423766613.0
Rounded with snprintf: 8631192423766613.0
Rounded with round, then divided: 8631192423766612.0
Opa Nosso snprintf
método retorna o resultado certo novamente, mas a abordagem de multiplicar, arredondar e dividir falha. Isso porque o valor matematicamente correta de 8631192423766613.0 * 100
, 863119242376661300.0
, não é exatamente representável como um duplo; o valor mais próximo é 863119242376661248.0
. Quando você divide isso de volta por 100, obtém 8631192423766612.0
- um número diferente do que você começou.
Espero que seja uma demonstração suficiente de que o uso roundf
para arredondar para um número de casas decimais está quebrado e que você deve usá-lo snprintf
. Se isso parecer um truque horrível para você, talvez você fique tranquilo ao saber que é basicamente o que o CPython faz .
Use float roundf(float x)
.
"As funções arredondadas arredondam seu argumento para o valor inteiro mais próximo no formato de ponto flutuante, arredondando os casos até a metade do zero, independentemente da direção de arredondamento atual." C11dr §7.12.9.5
#include <math.h>
float y = roundf(x * 100.0f) / 100.0f;
Dependendo da sua float
implementação, os números que podem parecer a meio caminho não são. como ponto flutuante é tipicamente orientado para a base 2. Além disso, o arredondamento preciso para o mais próximo 0.01
em todos os casos "na metade do caminho" é mais desafiador.
void r100(const char *s) {
float x, y;
sscanf(s, "%f", &x);
y = round(x*100.0)/100.0;
printf("%6s %.12e %.12e\n", s, x, y);
}
int main(void) {
r100("1.115");
r100("1.125");
r100("1.135");
return 0;
}
1.115 1.115000009537e+00 1.120000004768e+00
1.125 1.125000000000e+00 1.129999995232e+00
1.135 1.134999990463e+00 1.139999985695e+00
Embora "1.115" esteja "no meio do caminho" entre 1.11 e 1.12, quando convertido para float
, o valor é 1.115000009537...
e não é mais "no meio do caminho", mas mais próximo do 1.12 e arredondado para o mais próximo float
de1.120000004768...
"1.125" é "intermediário" entre 1.12 e 1.13, quando convertido para float
, o valor é exatamente 1.125
e é " intermediário ". Arredonda para 1,13 devido a vínculos com a regra par e arredonda para o mais próximo float
de1.129999995232...
Embora "1.135" esteja "no meio do caminho" entre 1,13 e 1,14, quando convertido para float
, o valor é 1.134999990463...
e não é mais "no meio do caminho", mas mais próximo do 1,13 e arredondado para o mais próximo float
de1.129999995232...
Se código usado
y = roundf(x*100.0f)/100.0f;
Apesar de "1.135" é "meio caminho" entre 1,13 e 1,14, quando convertido para float
, o valor é 1.134999990463...
e não é "meio caminho", mas mais perto de 1.13, mas incorretamente rodadas para float
de 1.139999985695...
, devido à precisão mais limitada de float
vs. double
. Esse valor incorreto pode ser visto como correto, dependendo das metas de codificação.
Eu fiz essa macro para arredondar números flutuantes. Adicione-o no seu cabeçalho / sendo do arquivo
#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))
Aqui está um exemplo:
float x = ROUNDF(3.141592, 100)
x é igual a 3,14 :)
double f_round(double dval, int n)
{
char l_fmtp[32], l_buf[64];
char *p_str;
sprintf (l_fmtp, "%%.%df", n);
if (dval>=0)
sprintf (l_buf, l_fmtp, dval);
else
sprintf (l_buf, l_fmtp, dval);
return ((double)strtod(l_buf, &p_str));
}
Aqui n
está o número de casas decimais
exemplo:
double d = 100.23456;
printf("%f", f_round(d, 4));// result: 100.2346
printf("%f", f_round(d, 2));// result: 100.23
dval
for enorme 3) o estranho if
/ else
bloco em que você faz exatamente a mesma coisa em cada ramificação e 4) o uso excessivamente complicado de sprintf
para criar o especificador de formato para uma segunda sprintf
chamada; é mais simples usar .*
e passar o valor duplo e o número de casas decimais como argumentos para a mesma sprintf
chamada.
#define roundz(x,d) ((floor(((x)*pow(10,d))+.5))/pow(10,d))
a = 8.000000
sqrt(a) = r = 2.828427
roundz(r,2) = 2.830000
roundz(r,3) = 2.828000
roundz(r,5) = 2.828430
Deixe-me primeiro tentar justificar minha razão para adicionar mais uma resposta a esta pergunta. Em um mundo ideal, arredondar não é realmente um grande problema. No entanto, em sistemas reais, você pode precisar lidar com vários problemas que podem resultar em arredondamentos que podem não ser o que você espera. Por exemplo, você pode estar executando cálculos financeiros em que os resultados finais são arredondados e exibidos aos usuários como duas casas decimais; esses mesmos valores são armazenados com precisão fixa em um banco de dados que pode incluir mais de duas casas decimais (por várias razões; não há um número ideal de locais a serem mantidos ... depende de situações específicas que cada sistema deve suportar, por exemplo, itens minúsculos cujos preços são frações de um centavo por unidade); e, cálculos de ponto flutuante executados em valores em que os resultados são mais / menos epsilon. Venho enfrentando essas questões e desenvolvendo minha própria estratégia ao longo dos anos. Não vou afirmar que enfrentei todos os cenários ou tenho a melhor resposta, mas abaixo está um exemplo da minha abordagem até agora que supera esses problemas:
Suponha que 6 casas decimais sejam consideradas como precisão suficiente para cálculos em flutuadores / duplos (uma decisão arbitrária para a aplicação específica), usando a seguinte função / método de arredondamento:
double Round(double x, int p)
{
if (x != 0.0) {
return ((floor((fabs(x)*pow(double(10.0),p))+0.5))/pow(double(10.0),p))*(x/fabs(x));
} else {
return 0.0;
}
}
O arredondamento para duas casas decimais para a apresentação de um resultado pode ser realizado da seguinte forma:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,8),6),2));
Pois val = 6.825
, o resultado é 6.83
o esperado.
Pois val = 6.824999
, o resultado é 6.82
. Aqui, a suposição é que o cálculo resultou exatamente 6.824999
e a sétima casa decimal é zero.
Pois val = 6.8249999
, o resultado é 6.83
. A sétima casa decimal, 9
nesse caso, faz com que a Round(val,6)
função dê o resultado esperado. Nesse caso, pode haver qualquer número de 9
s à direita .
Pois val = 6.824999499999
, o resultado é 6.83
. Arredondar para a oitava casa decimal como primeiro passo, ou seja Round(val,8)
, cuida do único caso desagradável pelo qual um resultado calculado de ponto flutuante calcula 6.8249995
, mas é representado internamente como 6.824999499999...
.
Finalmente, o exemplo da pergunta ... val = 37.777779
resulta em 37.78
.
Essa abordagem pode ser mais generalizada como:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,N+2),N),2));
onde N é precisão a ser mantida para todos os cálculos intermediários em flutuadores / duplos. Isso funciona com valores negativos também. Não sei se essa abordagem é matematicamente correta para todas as possibilidades.
Código C simples para arredondar um número:
float n = 3.56;
printf("%.f", n);
Isso produzirá:
4
... ou você pode fazer da maneira antiga, sem bibliotecas:
float a = 37.777779;
int b = a; // b = 37
float c = a - b; // c = 0.777779
c *= 100; // c = 77.777863
int d = c; // d = 77;
a = b + d / (float)100; // a = 37.770000;
Isso, é claro, se você deseja remover as informações extras do número.
esta função pega o número e a precisão e retorna o número arredondado
float roundoff(float num,int precision)
{
int temp=(int )(num*pow(10,precision));
int num1=num*pow(10,precision+1);
temp*=10;
temp+=5;
if(num1>=temp)
num1+=10;
num1/=10;
num1*=10;
num=num1/pow(10,precision+1);
return num;
}
ele converte o número do ponto flutuante em int, deslocando o ponto à esquerda e verificando a condição maior que cinco.
float
(edouble
) não são vírgulas flutuantes decimais - elas são vírgulas flutuantes binárias -, portanto, o arredondamento para as casas decimais não faz sentido. Você pode arredondar a saída, no entanto.