Quanto seria (200*0,7) + 200? 340 você diria? Então você deve ler este artigo. Dependendo da situação esta operação matemática não resultará em 340. Absurdo? Quem programa em linguagem de programação C deve ficar atento!
Um caso interessante porém é quando o C converte ponto flutuante para inteiro. Neste caso o C realiza a conversão simplesmente ignorando as casas decimais. Se o número em ponto flutuante é 2.34 o inteiro receberá apenas 2! E continuaria recebendo 2 mesmo que fosse 2.9999. O C NÃO REALIZA arredondamentos!
int x;
double d;
d = 2.23;
x = d; /* x tem 2 */
d = 2.999999999999999;
x = d; /* x tem 2!! */
Não importa o quão próximo esteja do 3, a conversão é sempre por truncamento das casas decimais. Coisas estranhas acontecem por causa da forma como o C converte números, como neste exemplo:
double x;
x = 99999 / 10000;
printf ("%lf\n", x);
Neste caso a divisão de 99999 por 10 mil deveria resultar em 9.9999. Como x é do tipo double, este valor poderia ser corretamente atribuído ao x. Ao invés disto, uma impressão de x resulta em apenas 9! Muito estranho, pois x poderia ter o 9,9999 pois suporta.
O problema é que houve uma divisão de um inteiro (99999) por outro inteiro (10000) e a resposta disto é um novo inteiro, o 9 por conta do truncamento. Somente após esta divisão é que o 9 é convertido para double e armazenado. Este erro é muito comum a programadores inexperientes e sua correção poderia ser simplesmente com:
double x;
x = 99999 / 10000.0;
/* o .0 indica que é double, logo já não são dois int dividindo */
printf ("%lf\n", x);
Ou fazer uso de cast, em uma conversão EXPLÍCITA.
double x;
x = (double)99999 / 10000;
/* instrui o C a converter 99999 para double antes da divisão */
printf ("%lf\n", x);
Voltando a nosso problema inicial, do 200*0,7, ainda não se tem explicações suficientes, pois mesmo sendo a multiplicação de um inteiro por um double, a resposta deveria resultar em um novo inteiro (140), sem casas decimais. Logo...
[2] Comentário enviado por jmsandy em 20/03/2008 - 17:16h
Opa, blza?
Este episódio não acontece somente com este produto. Pegando o seu exemplo 200 x 0.7, o fator 0.7, quase todos os seus múltiplos o valor é arredondado para 1 a menos.
Achei bastante interessante e testei aqui no trabalho em um compilador for Windows quando chegar em casa vou testar no Linux para tirar a tema. Mais observação muito interessante. Em determinados compiladores, como o dá Borland, existe um erro quanto a ponto flutuante que voce tem que declarar um prototipo de uma função para ele não acontecer. E támbém o flush(stdin) para limpar o buffer(ou alguma coisa parecida é que não me recordo muito bem).
Está de parabéns pela observação.
[3] Comentário enviado por antonioclj em 20/03/2008 - 17:47h
Mas isto vai acontecer mesmo. Você declarou um inteiro e faz uma multiplicação com ponto flutuante, vai dar inteiro na resposta. Você tem que usar o modificador float e não int. Bom isto é para os porgramadores de plantão.
[4] Comentário enviado por elgio em 20/03/2008 - 17:51h
Oi Antonio.
Sabemos disto.
Mas acho que tu não entendeu direito o FOCO que eu explorei.
No caso a multiplicacao 0.7*200 devia resultar em um inteiro redondo, assim como 0.6 * 100 que resulta em 60.
Não tem poblema algum misturar int com doubles ou float em C, desde que VOCÊ SAIBA o que está fazendo. Eu uso muito isto (mesmo que raramente saiba o que estou fazendo :-D)
[10] Comentário enviado por elgio em 21/03/2008 - 11:44h
Gabriel!.
Em arquiteturas 64 bits este erro não ocorre.
Não sei exatamente PORQUE NÃO OCORRE, pois testei em um servidor 64 bits que tenho acesso e vi que os arredondamentos SÃO FEITOS!!
Inclusive COM MESMA versão de GCC.
Meu notebook, 32 bits, GCC 4.1.2.
Tamanho de um double: 8 bytes
0.7 = 0.6999999999999999555910790149937383830547
Em um servidor 64 bits, GCC 4.1.2
Tamanho de um double: 8 bytes
0.7 = 0.6999999999999999555910790149937383830547
Mas no 64 bits o GCC informa CORRETAMENTE 340 como tu falou!
Se alguém sabe o motivo, poderia contribuir. Este GCC para 64 bits é o PRIMEIRO COMPILADOR C que faz o cálculo CORRETO! O Dev++, Borland e GCC 32 bits fazem errado.
[11] Comentário enviado por elgio em 21/03/2008 - 11:45h
pedroarthur.jedi: sim, eu conheço bem os erros de representação.
Tanto que no artigo eu descrevo isto como um erro.
O que ocorreu neste caso NÃO É UM MISTÉRIO. Só escrevi o artigo para que todos que programem em C tenham muito cuidado com estes erros.
[13] Comentário enviado por rui_alex em 21/03/2008 - 18:22h
[There was strong agreement in the C89 Committee that floating values should truncate toward zero when converted to an integer type, the specification adopted in the Standard.]
[Many results are exact or correctly rounded when computed with twice the number of digits of
precision as the data.]
-c99 Rationale-
Na arquitectura 64 bits suponho que os floats sejam pelo menos 8 bytes?
[14] Comentário enviado por elgio em 21/03/2008 - 20:30h
Bom...
O que eu descobri com um sizeof foi:
32 bits: int, long int e float em 32 bits. double em 64 bits, "long double" com 12 bytes (96 bits). Mas isto o que o gcc retorna, não quer dizer que seja o que a ULA e o co-processador suporta (alguém bom em Hardware ai?)
No 64 bits, MESMA VERSÃO de gcc, obtive: int e float em 32 bits, double em 64 bits e "long double" com 16 bytes (128 bits).
Mas é estranho, pois em ambas o problema de PRECISÃO EXISTE, isto é, 0.7 não é representável com PRECISÃO.
Por que diabos o gcc para 64 bits realiza algum ARREDONDAMENTO?
[16] Comentário enviado por luizpetiz em 22/03/2008 - 14:45h
É... legalzinho!
Psiu! Tô falando assim porque o cara é meu professor na ulbra. Não posso dizer que é um baita artigo e só um cara como ele poderia dedicar-se a esses assuntos e tal.., que ele é fera mesmo, se não ele se convence...
[18] Comentário enviado por estevao90 em 22/03/2008 - 21:01h
mt bom...
meu professor sempre alerta a gente quando a esse mistério do C
o negócio é: se for precisar de ponto flutuante, coloca todas variáveis envolvidas como float, por exemplo, facilita a vida! rsrs
[21] Comentário enviado por anonimo1234 em 29/03/2008 - 23:44h
Interessante,um bom exemplo da importancia da definição de tipos,pois o tipo apropriado para essa operação creio que seja float e não int,para não ficarmos sujeitos a arrendodamentos imperfeitos do C.
[22] Comentário enviado por SamL em 04/12/2008 - 23:21h
Eu executei o script e ocorreu o mesmo erro, 'a=339'
Mas consegui evitar isso fazendo assim:
#include <stdio.h>
int main()
{
int a;
a = 200;
a = (a *7)/10 + a;//ou a = (a*7/10) + a; ao inves de '(a*0.7)'
printf("O valor de a eh %d\n", a);
return 0;
}
e obtive o resultado correto:
'O valor de a eh 340'
#include <math.h>
#define ARRED(x) floor( x + 0.5 ) //Isso retorna um inteiro
#define ARRED2(x, z) floor( x*pow(10, z) + 0.5 )/pow(10, z) //sendo z o numero de casas decimais
Assim que for possível vou testar em casa no linux.