Página 1 de 2

ZERO que não é ZERO

Enviado: 04 Set 2007 14:31
por Eolo
(esta vai pro Maligno...)

Uma vez eu apanhei com uma comparação, onde o resultado de uma operação (a+b+c+d), que dava ZERO (no papel), resultava em .F. quando eu comparava com o número 0. Não lembro direito do detalhe, mas era algo do tipo:

a:=b:=10
c:=d:=-10
(a+b+c+d)=0 -> .F.

E isto porque (a+b+c+d) resultava em "0,000000000000001"...


Bão, eu dei uma pesquisada na época, achei algumas explicações, mas como elas eram 'muito' pro meu conhecimento de informática, abandonei a idéia de entender. E passei a usar algo que resolvia:
strzero(a+b+c+d,15,2)=strzero(0,15,2)


Nem sei porque lembrei disso agora, mas acho que é importante. Aliás, nem sei se soube iniciar a discussão! Alguém ae melhora a questão!

Enviado: 04 Set 2007 14:38
por Ademir
Uma vez tive este problema tambem. Passei a comparar round(valor1,2)=round(valor2,2) e aí funcionou. Tambem como vc, não tive tempo na epoca de verificar o porque disso. Pelo pouco que pesquisei, parece que tinha alguma coisa a ver com comparaçao de campos com variaveis.

Enviado: 04 Set 2007 15:06
por Stanis Luksys
Opa,

Isso acontece pela forma como o compilador trabalha com os números, e no caso de grandes mantissas é uma limitação até mesmo da própria da máquina.

No caso do Clipper, e do exemplo acima que é um número razoavelmente simples, um SET DECIMALS deve resolver.

Isso é estudado em teoria dos erros, que explica, no campo do cálculo numérico, como é feito o arredondamento em máquinas, através da aritmética do ponto flutuante.

Enviado: 04 Set 2007 15:23
por Eolo
SET DECIMALS define como os números são EXIBIDOS, não como eles são tratados... O número 0.000000000001, por exemplo, será simplesmente EXIBIDO como 0.00 com SET DECI TO 2 mas, internamente, o número vai continuar 0.000000000001.

Já o round() faz o mesmo número virar 0.00.

Enviado: 04 Set 2007 15:25
por Eolo
Eu to me referindo a "alguma outra coisa", que eu não lembro direito o que é.

Algo como 10/5 resultar em 2,000000000000000000000001 (e não em 2), isto como resultado da forma como as informações são tratadas (em bytes) pelos computadores...

Enviado: 04 Set 2007 15:56
por ederxc
O que o Vô quer saber é por que que isso acontece , como resolver ele ja disse que conseguil , caramba to curioso pra saber tbm , sera que não tem alguma explicação matematica pra isso? Assim como nosso amigo stanys diz , fica um pouco vago ....
:)Pos

Enviado: 04 Set 2007 17:08
por Stanis Luksys
Opa,

Existe sim uma explicação matemática, e é exatamente a partir do tema que eu já citei: teoria dos erros.

No estudo dessa parte do cálculo numérico verificamos quais são as possibilidades de arredondamento de um número segundo as regras estabalecidas pela capacidade da máquina (ou do soft no caso). Um exemplo claro disso é a calculadora comum, que possui apenas 8 dígitos.

O arredondamento no computador independe de ser realizada alguma operação artimética, ele sempre é executado quando se trata de pontos flutuantes. Existe uma fórmula que define o modo como é realizado o arrendodamento, e é bem grandinha...

Por exemplo, o número 5 em ponto flutuante é:
0,5000000... x10^1

Sendo que haverá depois da vírgula quantas casas a máquina "suportar", no caso dos computadores, tanto a máquina como o software influenciam. Este número de casas após a vírgula (o tamanho da mantissa, como eu disse la no outro post) também é uma das variáveis da tal fórmula...

Etc e tal....

Sei que num deu pra entender nada do que falei, mas enfim, é por aí caminho pra entender este erro, mais evidente no Clipper por não permitir a declaração direta de números float.

Falou!

Re: ZERO que não é ZERO

Enviado: 04 Set 2007 17:38
por Maligno
Eolo escreveu:Uma vez eu apanhei com uma comparação, onde o resultado de uma operação (a+b+c+d), que dava ZERO (no papel), resultava em .F. quando eu comparava com o número 0.
Na aritmética de inteiros, que são sempre maioria no computador, comparações desse tipo podem ser feitas sempre sem qualquer preocupação. O problema é que no Clipper, por não ser a XBase uma linguagem tipada, todos os números são em ponto flutuante. E nesse tipo de dado, zero nunca é zero. A solução é exatamente essa que você propôs: converter para string e comparar. A teoria disso é realmente bem complexa. Mas se você quiser um tutorial a respeito (é bem interessante), clique aqui.

Inclusive, em algumas linguagens tipadas, uma solução é simplesmente inferir: "se for menor que 0,0000001, considere como sendo igual zero". O quê, aliás, também pode ser feito no Clipper. Mas eu prefiro converter pra string. :)

Enviado: 04 Set 2007 18:03
por Stanis Luksys
Complementando,

Números em ponto flutantes obrigatoramente sempre tem o formato:

0,abcde . x ^ y

Onde a deve ser semprwe diferente de zero.

Por isso o menor número representado em ponto flutante é sempre 0,1 elevado a menor potência possível dentro das capacidades da máquina.

Exemplo:
0,100000 x 10 ^ (- 9999999999999999999999999999999999999)


Um número bem pequeno, mas nunca igual a zero....

Enviado: 04 Set 2007 18:23
por Maligno
Me lembrei agora dos tempos do bom e velho 8088, que não tinha núcleo de ponto flutuante e dependia do uso de um emulador (ainda presente no Clipper) ou de um co-processador, o 8087. Só tive um cliente que comprou. Era caro paca. :)

Enviado: 04 Set 2007 19:52
por Eolo
Valeu pelas informações.

Bem, a intenção era chamar a atenção (dos principiantes) para o cuidado que deve ser tomado em comparações com números...

Enviado: 04 Set 2007 21:06
por janio
Já tive um problema semelhante na divisão de 9,60 por 3 que ele teimava em dizer que cada parcela não era 3,20. Quando somado as tres parcelas e comparado ao valor inicial (9,60) nao batia.

Um colega postou uma função que ainda uso. Resolveu.

Código: Selecionar todos

*** PROCEDURE para Comparar duas variaveis numericas *** 
FUNCTION Compara(nValor1,nValor2) && COMPARA VALORES REAIS ... 
IF VAL(STR(nValor1)) == VAL(STR(nValor2)) 
   RETURN(.T.)  && OK ... 
ELSE 
   RETURN(.F.)  && PROBLEMAS ... 
END 
*** 
O Maligno à época também postou a função abaixo:

Código: Selecionar todos

function CompNum(v1,v2,nDec) 
default nDec to 2 
return  Str(v1*(10**nDec),18) == Str(v2*(10**nDec),18)
Jânio

Enviado: 04 Set 2007 21:52
por Maligno
function CompNum(v1,v2,nDec)
É essa mesma que eu uso. Prefiro ela, que é mais genérica (nDec mutável). Mas a função anterior resolve bem com valores monetários, já que os dois Val() reconvertem os valores que são arredondados igualmente pelas chamadas às Str(). Aí a comparação se torna precisa.

Enviado: 05 Set 2007 07:42
por ederxc
Pessoal , valeu ae pela explicação ;Só sei que o barato é loko mesmo...


-:]

Enviado: 05 Set 2007 11:21
por Maligno
O bom é que, uma vez programando em alto nível, não é necessário se preocupar com esses detalhes. Isso só aparece mais em um nível mais baixo. Mas ainda assim, é uma coisa interessante. :)