Pergunta

Considere o seguinte código:

0.1 + 0.2 == 0.3  ->  false
0.1 + 0.2         ->  0.30000000000000004

Por que essas imprecisões acontecer?

Foi útil?

Solução

matemática

ponto flutuante é assim. Na maioria das linguagens de programação, que se baseia na IEEE 754 padrão. JavaScript utiliza representação de ponto flutuante de 64-bits, o que é o mesmo que double do Java. O cerne do problema é que os números são representados neste formato como um todo número de vezes uma potência de dois; números racionais (como 0.1, que é 1/10) cujo denominador não é uma potência de dois não pode ser exatamente representada.

Para 0.1 no formato binary64 padrão, a representação pode ser escrito exatamente como

Em contraste, o 0.1 número racional, que é 1/10, pode ser escrita exatamente como

  • 0.1 em decimal, ou
  • 0x1.99999999999999...p-4 em um análogo de C99 notação hexfloat, onde o ... representa uma sequência sem fim de 9 de.

O 0.2 constantes e 0.3 em seu programa também será aproximações aos seus verdadeiros valores. Acontece que o double mais próximo 0.2 é maior do que o número 0.2 racional, mas que o double mais próximo 0.3 é menor do que o número 0.3 racional. A soma de 0.1 e 0.2 acaba sendo maior do que o número 0.3 racional e, portanto, em desacordo com a constante em seu código.

Um tratamento bastante abrangente de questões aritméticas em ponto flutuante é O que cada cientista computador deve saber sobre Floating-Point Arithmetic . Para obter uma explicação mais fácil de digerir, consulte floating-point-gui.de .

Nota lateral: Todos os posicionais (-N de bases) sistemas numéricos compartilhar esse problema com precisão

Plain decimal velha (base 10) números têm os mesmos problemas, razão pela qual os números como 1/3 acabar como 0,333333333 ...

Você só tropeçou em um número (3/10) que passa a ser fácil para representar com o sistema decimal, mas não se encaixa no sistema binário. Ele vai nos dois sentidos (a algum pequeno grau), bem como: 1/16 é um número feia em decimal (0,0625), mas em binário parece tão puro como um 10000 faz em decimal (0,0001) ** - se estivéssemos em o hábito de usar um sistema numérico de base 2 em nossas vidas diárias, você sequer olhar para esse número e, instintivamente, entendo que você pode chegar lá de reduzir para metade a alguma coisa, reduzir para metade isso de novo, e de novo e de novo.

** Claro, isso não é exatamente como números de ponto flutuante são armazenados na memória (eles usam uma forma de notação científica). No entanto, ele ilustrar o ponto de que os erros de precisão binária de ponto flutuante tendem a surgir porque os números "mundo real" estamos normalmente interessados ??em trabalhar com são tão frequentemente potências de dez - mas apenas porque usamos um dia- decimal number system hoje. É também por isso vamos dizer coisas como 71% em vez de "5 de cada 7" (71% é uma aproximação, já que 5/7 não pode ser representado exatamente com qualquer número decimal).

Portanto, não: números de ponto flutuante binários não são quebrados, eles só acontecerá a ser tão imperfeita quanto qualquer outro sistema numérico de base N:)

Side Side Nota: Trabalho com flutuadores na programação

Na prática, este problema de meios de precisão que você precisa para usar o arredondamento funções para arredondar o seu flutuadoring números de ponto fora a porém muitas casas decimais você estiver interessado em antes de exibi-los.

Você também precisará substituir testes de igualdade com as comparações que permitem uma certa quantidade de tolerância, o que significa:

Do não fazer if (float1 == float2) { ... }

Em vez disso fazer if (Math.Abs(float1 - float2) < myToleranceValue) { ... }.

myToleranceValue deve ser escolhido para a sua aplicação particular - e terá muito a ver com o quanto "espaço de manobra" você está preparado para permitir, e que o maior número você está indo para ser comparando pode ser (devido à perda de problemas de precisão). Cuidado com os "double.Epsilon" constantes de estilo no idioma de sua escolha (Number.EPSILON em Javascript). Estes são não para ser usado como valores de tolerância.

Mais informações sobre Tolerâncias:

(Shameless auto-promoção por um editor - pena do hijack)

Eu coloquei uma explicação mais detalhada de como escolher uma tolerância, e porque para evitar Number.EPSILON e sua laia em https://dev.to/alldanielscott/how-to-compare-numbers-correctly-in-javascript-1l4i

Outras dicas

A perspectiva de um Designer Hardware

Eu acredito que eu deveria adicionar perspectiva de um designer de hardware para isso desde que eu projetar e construir flutuante hardware ponto. Saber a origem do erro pode ajudar a compreender o que está acontecendo no software e, finalmente, espero que isso ajuda a explicar as razões pelas quais os erros de ponto flutuante acontecer e parecem se acumular ao longo do tempo.

1. Visão geral

Do ponto de vista de engenharia, operações de ponto flutuante mais terá algum elemento de erro uma vez que o hardware que faz os cálculos de ponto flutuante só é obrigado a ter um erro de menos de metade de uma unidade no último lugar. Portanto, tanto hardware vai parar em uma precisão que é apenas necessária para produzir um erro de menos de metade de uma unidade no último lugar para um única operação o que é especialmente problemático em flutuante divisão ponto. O que constitui uma única operação depende de quantos operandos a unidade leva. Para a maioria, é dois, mas algumas unidades tomar 3 ou mais operandos. Devido a isso, não há garantia de que as operações repetidas resultarão em um erro desejável, pois os erros somam ao longo do tempo.

2. Padrões

A maioria dos processadores seguir o IEEE-754 padrão, mas alguns usam denormalized, ou padrões diferentes . Por exemplo, existe um modo em desnormalizado IEEE-754 que permite a representação de números muito pequenos de ponto flutuante em detrimento da precisão. A seguir, no entanto, irá cobrir o modo normalizado de IEEE-754 que é o modo típico de operação.

No padrão IEEE-754, projetistas de hardware são permitidos qualquer valor de erro / epsilon contanto que é menos de metade de uma unidade no último lugar, eo resultado só tem de ser inferior a metade de uma unidade no último lugar para uma operação. Isso explica por que, quando há operações repetidas, os erros se somam. Para IEEE-754 precisão dupla, este é o bit 54, uma vez que 53 bits são utilizados para representar a parte numérica (normalizado), também chamada a mantissa, do número de ponto flutuante (por exemplo a 5,3 em 5.3e5). As próximas seções entrar em mais detalhes sobre as causas do erro de hardware em várias operações de ponto flutuante.

3. Causa de erro de arredondamento na Divisão

A principal causa do erro no flutuante divisão ponto é os algoritmos de divisão utilizados para calcular o quociente. A maioria dos sistemas de computador calcular divisão usando a multiplicação por uma inversa, principalmente em Z=X/Y, Z = X * (1/Y). A divisão é calculada de forma iterativa ou seja, cada ciclo calcula alguns pedaços do quociente até que a precisão desejada seja atingida, o que para IEEE-754 é qualquer coisa com um erro de menos de uma unidade no último lugar. A tabela de recíprocos de Y (1 / Y) é conhecida como a tabela de selecção quociente (QST) na divisão lenta, e o tamanho em bits da tabela de selecção quociente é geralmente a largura da raiz, ou um número de bits de o quociente calculado em cada iteração, além de alguns pedaços de guarda. Para o padrão IEEE-754, precisão dupla (64 bits), seria o tamanho da raiz do divisor, além de um pedaços de guarda alguns k, onde k>=2. Assim, por exemplo, uma tabela Quociente de selecção típicos para um divisor que calcula 2 bits do quociente de cada vez (Radix 4) seria bits de 2+2= 4 (além de alguns bits opcional).

3.1 Divisão de erro de arredondamento: aproximação das recíproco

O que recíprocos estão na tabela a seleção quociente depender do método de divisão : divisão lenta, tais como a divisão SRT, ou divisão rápida, como divisão Goldschmidt; cada entrada é modificado de acordo com o algoritmo de divisão em uma tentativa para se obter o menor erro possível. Em qualquer caso, porém, todos os recíprocos are aproximações do recíproco real e introduzir algum elemento de erro. Ambos os métodos de divisão e de divisão rápida lentas calcular o quociente de forma iterativa, isto é, um certo número de bits do quociente são calculados a cada passo, em seguida, o resultado é subtraído do dividendo, e o divisor repete os passos até que o erro é menor do que uma metade de um unidade no último lugar. métodos de divisão lenta calcular um número fixo de dígitos do quociente em cada passo e geralmente são menos caras de construir, e métodos de divisão rápida calcular um número variável de dígitos por passo e são geralmente mais caros de construir. A parte mais importante dos métodos de divisão é que a maioria deles dependem de multiplicação repetida por um aproximação de uma recíproca, para que eles são propensos a erro.

4. Erros de arredondamento em outras operações: truncamento

Outra causa dos erros de arredondamento em todas as operações são os diferentes modos de truncamento da resposta final que o IEEE-754 permite. Há truncado, round-para-a-zero, round-a-mais próximo (padrão), redondo -down, e round-up. Todos os métodos de introduzir um elemento de erro de menos de uma unidade no último lugar para uma única operação. Com o tempo e as operações repetidas, truncamento também adiciona cumulativamente ao erro resultante. Este erro de truncamento é especialmente problemático em exponenciação, que envolve alguma forma de multiplicação repetida.

5. Operações repetidas

Uma vez que o hardware que faz os cálculos de ponto flutuante só precisa produzir um resultado com um erro de menos de metade de uma unidade no último lugar para uma única operação, o erro vai crescer ao longo operações repetido se não for observada. Esta é a razão que nos cálculos que exigem um erro limitado, os matemáticos usam métodos como o uso do noreferrer round-a-mais próximo mesmo dígitos no último lugar de IEEE-754, porque, ao longo do tempo, os erros são mais propensos a se anulam mutuamente, e Intervalo Aritmética combinado com variações do IEEE 754 modos de arredondamento para prever erros de arredondamento, e corrigi-los. Devido ao seu baixo erro relativo em comparação com outros modos de arredondamento, e volta para dígito ainda mais próximo (em último lugar), é o modo de arredondamento padrão IEEE-754.

Note que o modo de arredondamento padrão, redondo-se mais próximo dígitos, mesmo no último lugar , garante um erro de menos de metade de uma unidade no último lugar para uma operação. Usando o truncamento, round-up, e só volta para baixo pode resultar em um erro que é maior do que a metade de uma unidade no último lugar, mas menos de uma unidade no último lugar, para que estes modos não são recomendados a menos que sejam utilizado no intervalo de aritmética.

6. Resumo

Em suma, a razão fundamental para os erros em operações de ponto flutuante é uma combinação do truncamento em hardware, e o truncamento de uma reciprocidade no caso de divisão. Desde o padrão IEEE-754 requer apenas um erro de menos de metade de uma unidade no último lugar para uma única operação, os erros de ponto flutuante operações mais repetidas irá somar menos que corrigido.

Quando você converte .1 ou 1/10 a base 2 (binário), obtém um padrão de repetição depois do ponto decimal, assim como tentando representar 1/3 na base 10. O valor não é exato, e, portanto, você pode 't fazer matemática exata com ele usando normais métodos de ponto flutuante.

A maioria das respostas aqui abordar esta questão em muito seco, termos técnicos. Eu gostaria de abordar isso em termos que os seres humanos normais podem entender.

Imagine que você está tentando fatia de pizzas. Você tem um cortador de pizza robótico que pode cortar fatias de pizza exatamente na metade. Ele pode reduzir pela metade uma pizza inteira, ou pode reduzir pela metade uma fatia existente, mas em qualquer caso, a redução para metade é sempre exato.

Isso cortador de pizza tem movimentos muito finas, e se você começar com uma pizza inteira, então reduzir pela metade disso, e continuar a reduzir para metade a menor fatia de cada vez, você pode fazer a redução para metade 53 vezes antes da fatia é muito pequeno, mesmo para as suas capacidades de alta precisão. Nesse ponto, você não pode mais Halve essa fatia muito fina, mas deve incluir ou excluir lo como está.

Agora, como é que você peça todas as fatias de tal forma que se somam a um décimo (0,1) ou um quinto (0,2) de uma pizza? Realmente pensar sobre isso, e tentar trabalhar para fora. Você pode até tentar usar uma verdadeira pizza, se você tiver um cortador de pizza precisão mítico na mão. : -)


A maioria dos programadores experientes, é claro, conhecer a verdadeira resposta, que é que não há nenhuma maneira de juntar um exata décimo ou quinto da pizza usando essas fatias, não importa quão finamente você fatia eles. Você pode fazer uma boa aproximação bonita, e se você somar à aproximação de 0,1 com a aproximação de 0,2, você tem uma boa aproximação muito de 0,3, mas ainda é apenas isso, uma aproximação.

Para números de precisão dupla (que é a precisão que permite reduzir para metade a sua pizza 53 vezes), os números imediatamente menor e maior que 0,1 são 0,09999999999999999167332731531132594682276248931884765625 e 0,1000000000000000055511151231257827021181583404541015625. O último é um pouco mais perto de 0,1 do que o anterior, de modo que um analisador numérico irá, dada uma entrada de 0,1, favorecer a última.

(A diferença entre esses dois números é o "menor fatia" que devemos decidir ou incluem, o que introduz um viés de alta, ou excluir, que introduz um viés de baixa. O termo técnico para esse menor fatia é um ulp .)

No caso do 0.2, os números são todos o mesmo, apenas ampliados por um fator de 2. Mais uma vez, nós favorecemos o valor que é ligeiramente maior do que 0,2.

Observe que em ambos os casos, as aproximações de 0,1 e 0,2 têm uma ligeira tendência para cima. Se somarmos o suficiente desses preconceitos em, eles vão empurrar o número cada vez mais longe do que queremos, e, na verdade, no caso de 0,1 + 0,2, o viés é de alta o suficiente para que o número resultante não é mais o número mais próximo a 0,3.

Em particular, 0,1 + 0,2 é realmente 0.1000000000000000055511151231257827021181583404541015625 + 0.200000000000000011102230246251565404236316680908203125 = 0.3000000000000000444089209850062616169452667236328125, ao passo que o número mais próximo de 0,3 é realmente 0,299999999999999988897769753748434595763683319091796875.


P.S. Algumas linguagens de programação também fornecem cortadores de pizza que pode em décimos exatas . Embora tais cortadores de pizza são incomuns, se você tem acesso a um, você deve usá-lo quando ele é importante ser capaz de obter exatamente um décimo ou um quinto de uma fatia.

(Originalmente publicado em Quora.)

ponto flutuante erros de arredondamento. 0,1 não pode ser representada mais exacta em base 2 como na base 10 devido à falta de factor primo 5. Tal como 1/3 leva um número infinito de dígitos para representar em decimal, mas é "0,1" na-base 3, 0,1 assume um número infinito de dígitos na base 2 em que não ocorre na base 10. E computadores não tem uma quantidade infinita de memória.

Além das outras respostas corretas, você pode querer considerar escalar seus valores para evitar problemas com aritmética de ponto flutuante.

Por exemplo:

var result = 1.0 + 2.0;     // result === 3.0 returns true

... em vez de:

var result = 0.1 + 0.2;     // result === 0.3 returns false

A expressão 0.1 + 0.2 === 0.3 retornos false em JavaScript, mas felizmente inteiro aritmética em ponto flutuante é exata, erros de representação tão decimais pode ser evitado por meio de extrapolação.

Como um exemplo prático, para evitar problemas de ponto flutuante onde a precisão é fundamental, é recomendado 1 para lidar com dinheiro como um inteiro representando o número de centavos: centavos 2550 em vez de dólares 25.50.


1 Douglas Crockford: JavaScript: The Good Parts : Apêndice A - Peças Awful (página 105) .

A minha resposta é bastante longa, então eu dividi-lo em três seções. Desde a pergunta é sobre a matemática de ponto flutuante, eu coloquei a ênfase em que a máquina realmente faz. Eu também fez específica para o dobro (64 bits) de precisão, mas o argumento aplica-se igualmente a qualquer aritmética de ponto flutuante.

Preâmbulo

Um IEEE 754 de precisão dupla binária de ponto flutuante formato (binary64) número representa um número do formulário

valor = (-1) ^ s * (1.m 51 m 50 ... m 2 m 1 m 0 ) 2 * 2 e-1023

em 64 bits:

  • O primeiro bit é o bit de sinal : 1 se o número for negativo, 0 caso contrário < sup> 1 .
  • Os próximos 11 bits são o expoente , que é compensado por 1023. Em outras palavras, depois de ler os bits expoente de um número de precisão dupla, 1023 deve ser subtraído para obter o poder de dois.
  • Os restantes 52 bits são o significando (ou mantissa). No mantissa, um 1. 'implícita' é sempre 2 omitido uma vez que o bit mais significativo de qualquer valor binário é 1.

1 - IEEE 754 permite o conceito de um assinado de zero - +0 e -0 são tratados de forma diferente: 1 / (+0) é infinito positivo; 1 / (-0) é infinito negativo. Para valores zero, a mantissa e expoente bits são todos zero. Nota: zero valores (+0 e -0) explicitamente não são classificados como denormal 2

.

2 - Este não é o caso para denormal números , que tem um expoente de deslocamento zero (e um 0. implícita). A gama de números de precisão dupla denormal é d min = | x | = d max , onde d min (o menor número diferente de zero representável) é 2 -1.023-51 (˜ 4,94 * 10 - 324 ) e d max (o maior número denormal, para o qual a mantissa consiste inteiramente de 1s) é 2 + 1 -1023 - 2 - 1023 -. 51 (˜ 2.225 * 10 -308 )


Transformar um número de precisão dupla para binário

Existem muitos conversores on-line para converter um duplo precisão número de ponto flutuante para binário (por exemplo, em binaryconvert.com ), mas aqui está um código de exemplo C # para obter a representação IEEE 754 para um número de precisão dupla (I separar as três partes, com dois pontos (:):

public static string BinaryRepresentation(double value)
{
    long valueInLongType = BitConverter.DoubleToInt64Bits(value);
    string bits = Convert.ToString(valueInLongType, 2);
    string leadingZeros = new string('0', 64 - bits.Length);
    string binaryRepresentation = leadingZeros + bits;

    string sign = binaryRepresentation[0].ToString();
    string exponent = binaryRepresentation.Substring(1, 11);
    string mantissa = binaryRepresentation.Substring(12);

    return string.Format("{0}:{1}:{2}", sign, exponent, mantissa);
}

Chegar ao ponto: a pergunta original

(Ir para o fundo para o TL; DR versão)

Cato Johnston (o consulente pergunta) perguntou por 0,1 + 0,2! = 0,3.

Escrito em binário (com dois pontos separam os três partes), o IEEE 754 representações dos valores são:

0.1 => 0:01111111011:1001100110011001100110011001100110011001100110011010
0.2 => 0:01111111100:1001100110011001100110011001100110011001100110011010

Note que a mantissa é composta de dígitos do 0011 recorrentes. Esta é tecla a razão pela qual não há qualquer erro com os cálculos - 0,1, 0,2 e 0,3 não pode ser representado em binário precisamente em finito número de bits binários mais do que 1/9, 1/3 ou 1/7 podem ser representados com precisão no decimal dígitos .

Observe também que podemos diminuir o poder no expoente por 52 e deslocar o ponto na representação binária para a direita por 52 lugares (muito parecido com 10 -3 * 1,23 == 10 -5 * 123). Isso, então, nos permite representar a representação binária como o valor exato que ela representa na forma de um * 2 p . onde 'a' é um inteiro.

Convertendo os expoentes decimal para, a remoção do deslocamento, e a inserção das 1 implícita (entre parêntesis rectos), 0,1 e 0,2 são:

0.1 => 2^-4 * [1].1001100110011001100110011001100110011001100110011010
0.2 => 2^-3 * [1].1001100110011001100110011001100110011001100110011010
or
0.1 => 2^-56 * 7205759403792794 = 0.1000000000000000055511151231257827021181583404541015625
0.2 => 2^-55 * 7205759403792794 = 0.200000000000000011102230246251565404236316680908203125

Para adicionar dois números, as necessidades expoente a ser o mesmo, ou seja:.

0.1 => 2^-3 *  0.1100110011001100110011001100110011001100110011001101(0)
0.2 => 2^-3 *  1.1001100110011001100110011001100110011001100110011010
sum =  2^-3 * 10.0110011001100110011001100110011001100110011001100111
or
0.1 => 2^-55 * 3602879701896397  = 0.1000000000000000055511151231257827021181583404541015625
0.2 => 2^-55 * 7205759403792794  = 0.200000000000000011102230246251565404236316680908203125
sum =  2^-55 * 10808639105689191 = 0.3000000000000000166533453693773481063544750213623046875

Uma vez que a soma não é da forma 2 n * 1. {bbb} aumentamos o expoente por um e mudar o decimal ( binário ) ponto para obter:

sum = 2^-2  * 1.0011001100110011001100110011001100110011001100110011(1)
    = 2^-54 * 5404319552844595.5 = 0.3000000000000000166533453693773481063544750213623046875

Existem agora 53 bits na mantissa (a 53 está em colchetes na linha acima). O modo padrão arredondamento para IEEE 754 é ' Round a mais próxima ' - ou seja, se um número x cai entre dois valores um e b , o valor onde o bit menos significativo é zero é escolhido.

a = 2^-54 * 5404319552844595 = 0.299999999999999988897769753748434595763683319091796875
  = 2^-2  * 1.0011001100110011001100110011001100110011001100110011

x = 2^-2  * 1.0011001100110011001100110011001100110011001100110011(1)

b = 2^-2  * 1.0011001100110011001100110011001100110011001100110100
  = 2^-54 * 5404319552844596 = 0.3000000000000000444089209850062616169452667236328125

Note que a e b diferem apenas no último bit; ...0011 + 1 = ...0100. Neste caso, o valor com o bit menos significativo do zero é b , então a soma é:

sum = 2^-2  * 1.0011001100110011001100110011001100110011001100110100
    = 2^-54 * 5404319552844596 = 0.3000000000000000444089209850062616169452667236328125

Considerando que a representação binária de 0,3 é:

0.3 => 2^-2  * 1.0011001100110011001100110011001100110011001100110011
    =  2^-54 * 5404319552844595 = 0.299999999999999988897769753748434595763683319091796875

que apenas difere da representação binária da soma de 0,1 e 0,2 por 2 -54 .

A representação binária de 0,1 e 0,2 são o mais preciso representações dos números permitido pelo IEEE 754. A adição destes representação, devido ao arredondamento modo padrão, resulta em um valor que apenas difere no menos significativo-bit.

TL; DR

Escrevendo 0.1 + 0.2 em uma representação binária IEEE 754 (com dois pontos separam os três partes) e comparando-a com 0.3, este é (Eu coloquei os pedaços distintos entre colchetes):

0.1 + 0.2 => 0:01111111101:0011001100110011001100110011001100110011001100110[100]
0.3       => 0:01111111101:0011001100110011001100110011001100110011001100110[011]

voltar convertido para decimal, estes valores são:

0.1 + 0.2 => 0.300000000000000044408920985006...
0.3       => 0.299999999999999988897769753748...

A diferença é exatamente 2 -54 , que é ~ 5,5511151231258 × 10 -17 -. Insignificante (para muitas aplicações) quando comparado com os valores originais

Comparando os últimos pedaços de um número de ponto flutuante é inerentemente perigoso, como quem lê o famoso " o que cada cientista computador deve saber sobre Floating-Point Arithmetic " (que abrange todas as principais partes desta resposta) vai saber.

A maioria das calculadoras usar dígitos adicionais guarda para contornar este problema, que é como 0.1 + 0.2 daria 0.3:. os poucos bits finais são arredondados

números de ponto flutuante armazenados no computador consistem em duas partes, um número inteiro e um expoente que a base é levado para e multiplicado pela parte inteira.

Se o computador estava trabalhando na base 10, 0.1 seria 1 x 10⁻¹, 0.2 seria 2 x 10⁻¹ e 0.3 seria 3 x 10⁻¹. Integer matemática é fácil e precisa, assim que adicionar 0.1 + 0.2 irá, obviamente, resultar em 0.3.

Os computadores não funcionam normalmente na base 10, eles trabalham na base 2. Você ainda pode obter resultados exatos para alguns valores, por exemplo 0.5 é 1 x 2⁻¹ e 0.25 é 1 x 2⁻², e adicioná-los resulta em 3 x 2⁻², ou 0.75. Exatamente.

O problema surge com números que podem ser representados exatamente na base 10, mas não na base 2. Esses números precisam ser arredondados para o seu equivalente mais próximo. Assumindo que o formato de ponto flutuante muito comum IEEE 64 bits, o número mais próximo de 0.1 é 3602879701896397 x 2⁻⁵⁵, e o número mais próximo de 0.2 é 7205759403792794 x 2⁻⁵⁵; juntá-las resulta em 10808639105689191 x 2⁻⁵⁵, ou um valor decimal exata do 0.3000000000000000444089209850062616169452667236328125. Números de ponto flutuante são geralmente arredondado para exibição.

flutuante erro de arredondamento ponto. De O que cada cientista computador deve saber sobre Floating-Point Arithmetic :

Espremendo infinitamente muitos números reais em um número finito de bits requer uma representação aproximada. Embora existem infinitos números inteiros, na maioria dos programas o resultado de cálculos inteiros podem ser armazenados em 32 bits. Em contraste, dado qualquer número fixo de bits, a maioria dos cálculos com números reais irá produzir quantidades que não pode ser exatamente representados com essa muitos bits. Por conseguinte, o resultado de um cálculo de ponto flutuante deve muitas vezes ser arredondados, a fim de se adaptar à sua representação finito. Este erro de arredondamento é o traço característico de ponto flutuante computação.

Meu solução alternativa:

function add(a, b, precision) {
    var x = Math.pow(10, precision || 2);
    return (Math.round(a * x) + Math.round(b * x)) / x;
}

precisão refere-se ao número de dígitos que você deseja preservar depois do ponto decimal durante a adição.

Um monte de boas respostas foram publicadas, mas eu gostaria de acrescentar mais um.

Nem todos os números pode ser representado através de flutua / duplos Por exemplo, o número "0.2" será representado como "0,200000003" em precisão simples no padrão ponto IEEE754 float.

Modelo para armazenar números reais sob o capô representar números flutuador como

 enter descrição da imagem aqui

Mesmo que você pode digitar 0.2 facilmente, FLT_RADIX e DBL_RADIX é 2; não 10 para um computador com FPU que usa "IEEE padrão para Binary Floating-Point Arithmetic (ISO / IEEE Std 754-1985)".

Por isso, é um pouco difícil para representar tais números exatamente. Mesmo se você especificar esta variável explicitamente sem qualquer cálculo intermediário.

Algumas estatísticas relacionadas a esta pergunta famosa dupla precisão.

Ao adicionar todos os valores ( a + b ) usando um passo de 0,1 (de 0,1 a 100), temos oportunidade ~ 15% de erro de precisão . Note-se que o erro pode resultar em valores ligeiramente maiores ou menores. Aqui estão alguns exemplos:

0.1 + 0.2 = 0.30000000000000004 (BIGGER)
0.1 + 0.7 = 0.7999999999999999 (SMALLER)
...
1.7 + 1.9 = 3.5999999999999996 (SMALLER)
1.7 + 2.2 = 3.9000000000000004 (BIGGER)
...
3.2 + 3.6 = 6.800000000000001 (BIGGER)
3.2 + 4.4 = 7.6000000000000005 (BIGGER)

Quando subtraindo todos os valores de ( a - b onde a> b ) usando um passo de 0,1 (de 100 a 0,1), temos ~ 34% de erro de precisão . Aqui estão alguns exemplos:

0.6 - 0.2 = 0.39999999999999997 (SMALLER)
0.5 - 0.4 = 0.09999999999999998 (SMALLER)
...
2.1 - 0.2 = 1.9000000000000001 (BIGGER)
2.0 - 1.9 = 0.10000000000000009 (BIGGER)
...
100 - 99.9 = 0.09999999999999432 (SMALLER)
100 - 99.8 = 0.20000000000000284 (BIGGER)

* 15% e 34% são de fato enorme, por isso sempre usar BigDecimal quando a precisão é de grande importância. Com 2 dígitos decimais (passo de 0,01) a situação agrava um pouco mais (18% e 36%).

Não, não quebrado, mas a maioria das frações decimais devem ser aproximadas

Resumo

aritmética de ponto flutuante é exata, infelizmente, não combinam bem com a nossa habitual representação base 10 número, de modo que parece que são muitas vezes dando-lhe entrada que é um pouco fora do que nós escrevemos.

Mesmo números simples como 0,01, 0,02, 0,03, 0,04 ... 0,24 não são representable exatamente como frações binárias. Se você contar até 0,01, 0,02, 0,03 ..., não até chegar a 0,25 você vai ter o primeiro representável fração da base 2 . Se você tentou que o uso de FP, o seu 0,01 teria sido um pouco fora, então a única maneira de adicionar 25 deles até um bom exata 0,25 teria exigido uma longa cadeia de causalidade envolvendo pedaços de guarda e arredondamento. É difícil prever para que levantar nossas mãos e dizer "FP é inexata", , mas isso não é realmente verdade.

Estamos constantemente a dar algo hardware FP que parece simples na base 10, mas é uma fração repetindo na base 2.

Como isso aconteceu?

Quando escrever em decimal, cada fração (especificamente, cada terminação decimal) é um número racional da forma

a / (2 n x 5 m )

Em binário, nós só temos o 2 n prazo, isto é:

a / 2 n

Assim, em decimal, não podemos representar 1 / 3 . Porque base 10 inclui 2 como um fator primordial, cada número podemos escrever como uma fração binária também pode ser escrita como uma fração base 10. No entanto, quase nada se escreve como base 10 fração é representável em binário. Na gama de 0,01, 0,02, 0,03 ... 0,99, única três números pode ser representado no nosso formato PF: 0,25, 0,50, e 0,75, porque eles são 1/4, 1/2, e 3/4, todos os números com um fator primordial usando apenas o n termo 2.

Na base de 10 que pode não representar 1 / 3 . Mas em binário, não podemos fazer 1 / 10 ou 1 / 3 .

Assim, enquanto cada fração binário pode ser escrito em decimal, o inverso não é verdadeiro. E, na verdade a maioria das frações decimais repetir em binário.

Lidar com ele

Os desenvolvedores geralmente são instruídos a fazer comparações, o conselho melhor poderia ser a de volta para valores integrais (na biblioteca C: round () e roundf (), ou seja, estadia no formato FP ) e, em seguida, comparar. Completando a uma fracção decimal específico resolve a maioria dos problemas com comprimento de saída.

Além disso, sobre os problemas de trituração de números reais (os problemas que FP foi inventado para no início, computadores terrivelmente caro) as constantes físicas do universo e todas as outras medições só são conhecidas por um número relativamente pequeno de algarismos significativos, de modo que o todo espaço do problema era "inexata" de qualquer maneira. FP "precisão" não é um problema neste tipo de aplicação.

A questão realmente surge quando as pessoas tentam usar FP para contagem de feijão. Ele faz o trabalho para isso, mas só se você ficar com valores integrais, que tipo de derrotas a ponto de usá-lo. É por isso que temos todas essas bibliotecas de software fração decimal.

Eu amo a resposta Pizza por Chris , porque descreve o problema real, não apenas o habitual handwaving sobre "imprecisão". Se FP eram simplesmente "imprecisa", poderíamos correção que e teria feito isso há décadas. A razão pela qual não tem é porque o formato FP é compacto e rápido e é a melhor maneira de mastigar um monte de números. Além disso, é um legado da corrida era espacial e os braços e as primeiras tentativas de resolver grandes problemas com computadores muito lentas usando sistemas de memória pequenos. (Às vezes, individuais núcleos magnéticos de armazenamento de 1 bit para, Mas isso é outra história. )

Conclusão

Se você está apenas contando feijão em um banco, as soluções de software que usam representações de seqüência decimais, em primeiro lugar de trabalho perfeitamente. Mas você não pode fazer cromodinâmica quântica ou aerodinâmica dessa forma.

Você tentou a solução de fita adesiva?

Tente determinar quando ocorrem erros e corrigi-los com short if, não é bonito, mas para alguns problemas que é a única solução e esta é uma delas.

 if( (n * 0.1) < 100.0 ) { return n * 0.1 - 0.000000000000001 ;}
                    else { return n * 0.1 + 0.000000000000001 ;}    

Eu tive o mesmo problema em um projeto de simulação científica no c #, e posso dizer-lhe que se você ignorar o efeito borboleta que vai recorrer a um grande dragão de gordura e morder-lhe no a **

Esses números estranhos aparecem porque os computadores usam binário (base 2) sistema de numeração para fins de cálculo, enquanto nós usamos decimal (base 10).

Há uma maioria de números fracionários que não pode ser representado com precisão tanto em binário ou em decimal ou ambos. Resultado -. A arredondado para cima (mas preciso) resultados número

A fim de oferecer O melhor solução eu posso dizer que eu descobri seguinte método:

parseFloat((0.1 + 0.2).toFixed(10)) => Will return 0.3

Deixe-me explicar por que é a melhor solução. Como outros mencionado em respostas acima é uma boa idéia usar pronto para usar a função Javascript toFixed () para resolver o problema. Mas o mais provável é que você vai encontrar com alguns problemas.

Imagine que você está indo para somar dois números flutuar como 0.2 e 0.7 aqui está:. 0.2 + 0.7 = 0.8999999999999999

O resultado esperado era 0.9 isso significa que você precisa de um resultado com precisão de 1 dígito neste caso. Então você deve ter (0.2 + 0.7).tofixed(1) usado mas você não pode apenas dar um certo parâmetro para toFixed (), uma vez que depende do número dado, por exemplo

`0.22 + 0.7 = 0.9199999999999999`

Neste exemplo, você precisa de precisão 2 dígitos por isso deve ser toFixed(2), de modo que deve ser o parâmetro para caber a cada determinado número flutuante?

Você pode dizer que seja de 10 em todas as situações, em seguida:

(0.2 + 0.7).toFixed(10) => Result will be "0.9000000000"

Droga! O que você vai fazer com esses zeros indesejados após 9? É o tempo para convertê-lo para flutuar para torná-lo como você deseja:

parseFloat((0.2 + 0.7).toFixed(10)) => Result will be 0.9

Agora que você encontrou a solução, é melhor oferecê-lo como uma função como esta:

function floatify(number){
           return parseFloat((number).toFixed(10));
        }

Vamos experimentar a si mesmo:

function floatify(number){
       return parseFloat((number).toFixed(10));
    }
 
function addUp(){
  var number1 = +$("#number1").val();
  var number2 = +$("#number2").val();
  var unexpectedResult = number1 + number2;
  var expectedResult = floatify(number1 + number2);
  $("#unexpectedResult").text(unexpectedResult);
  $("#expectedResult").text(expectedResult);
}
addUp();
input{
  width: 50px;
}
#expectedResult{
color: green;
}
#unexpectedResult{
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="number1" value="0.2" onclick="addUp()" onkeyup="addUp()"/> +
<input id="number2" value="0.7" onclick="addUp()" onkeyup="addUp()"/> =
<p>Expected Result: <span id="expectedResult"></span></p>
<p>Unexpected Result: <span id="unexpectedResult"></span></p>

Você pode usá-lo desta maneira:

var x = 0.2 + 0.7;
floatify(x);  => Result: 0.9

Como w3schools sugere que há uma outra solução também, você pode multiplicar e dividir para resolver o problema acima:

var x = (0.2 * 10 + 0.1 * 10) / 10;       // x will be 0.3

Tenha em mente que (0.2 + 0.1) * 10 / 10 não irá funcionar em todos embora pareça o mesmo! Eu prefiro a primeira solução desde que eu posso aplicá-lo como uma função que converte a bóia de entrada para flutuar saída precisa.

Uma vez que ninguém mencionou isso ...

Algumas linguagens de alto nível como Python e Java vêm com ferramentas para superar as limitações de ponto flutuante binários. Por exemplo:

  • Python do decimal módulo e Java de classe BigDecimal , que representam números internamente com notação decimal (ao contrário de binário notação). Ambos têm precisão limitada, de modo que eles ainda estão sujeitos a erros, no entanto, resolver os problemas mais comuns com binário aritmética de ponto flutuante.

    Os decimais estão muito agradável quando se trata de dinheiro: centavos mais de dez vinte centavos são sempre exatamente trinta centavos:

    >>> 0.1 + 0.2 == 0.3
    False
    >>> Decimal('0.1') + Decimal('0.2') == Decimal('0.3')
    True
    

    módulo decimal do Python é baseado em IEEE padrão 854-1987 .

  • O Python fractions módulo e do Apache Common classe BigFraction . Ambos representam números racionais como pares (numerator, denominator) e eles podem dar resultados mais precisos do que decimal aritmética de ponto flutuante.

Nenhuma destas soluções é perfeito (especialmente se olharmos para performances, ou se exigem uma precisão muito elevada), mas ainda assim eles resolvem um grande número de problemas com binário aritmética de ponto flutuante.

Muitos dos numerosos duplicatas deste pergunta perguntar sobre os efeitos de ponto flutuante arredondamento em números específicos. Na prática, é mais fácil obter uma sensação de como ele funciona, olhando para os resultados exatos dos cálculos de interesse em vez de apenas lendo sobre ele. Algumas linguagens fornecem maneiras de fazer isso -. Tais como a conversão de um float ou double para BigDecimal em Java

Uma vez que esta é uma questão da língua-agnóstico, ele precisa de ferramentas de linguagem agnóstico, tais como um decimal de ponto flutuante Converter .

Aplicando-lo para os números em questão, tratados como duplos:

0.1 convertidos ao 0,1000000000000000055511151231257827021181583404541015625,

0,2 convertidos ao 0,200000000000000011102230246251565404236316680908203125,

0,3 convertidos ao 0,299999999999999988897769753748434595763683319091796875, e

0.30000000000000004 convertidos ao 0,3000000000000000444089209850062616169452667236328125.

Adicionando os dois primeiros números manualmente ou em uma calculadora decimal, como completa Calculator Precision , mostra a soma exata das entradas reais é 0,3000000000000000166533453693773481063544750213623046875.

Se fosse arredondado para o equivalente a 0,3 do erro de arredondamento seria 0,0000000000000000277555756156289135105907917022705078125. Arredondamento para o equivalente a 0,30000000000000004 também dá erro de arredondamento 0,0000000000000000277555756156289135105907917022705078125. O desempate round-a-mesmo se aplica.

Voltando ao conversor de ponto flutuante, o hexadecimal cru para 0,30000000000000004 é 3fd3333333333334, que termina em um dígito mesmo e, por conseguinte, é o resultado correcto.

Can I basta adicionar; as pessoas sempre assumem que este é um problema no computador, mas se você contar com as mãos (base 10), você não pode obter (1/3+1/3=2/3)=true menos que você tenha o infinito para adicionar 0,333 ... para 0.333 ... então, assim como com o problema (1/10+2/10)!==3/10 na base 2, você truncar-lo para 0,333 + 0,333 = 0,666 e, provavelmente, em volta dele para 0,667 que também seria ser tecnicamente impreciso.

Conde em ternário, e terços não são um problema embora - talvez alguma corrida com 15 dedos em cada mão iria perguntar por que sua matemática decimal foi quebrado ...

O tipo de ponto flutuante matemática que pode ser implementado em um computador digital utiliza necessariamente uma aproximação dos números reais e operações sobre eles. (O padrão Versão corre para mais de cinqüenta páginas de documentação e tem um comitê para lidar com a sua errata e refinamento.)

Esta aproximação é uma mistura de aproximações de diferentes tipos, cada um dos quais pode ser ignorado ou cuidadosamente representaram devido à sua forma específica de desvio de exactidão. Ele também envolve uma série de casos excepcionais explícitas, tanto a nível de hardware e software que a maioria das pessoas caminhada passado direita, enquanto fingindo não perceber.

Se você precisar de precisão infinita (usando o p número, por exemplo, em vez de um de seus muitos stand-ins mais curtos), você deve escrever ou usar um programa de matemática simbólica vez.

Mas se você está bem com a ideia de que de ponto flutuante por vezes, a matemática é difusa em valor e lógica e os erros podem se acumular rapidamente, e você pode escrever seus requisitos e testes para permitir que, em seguida, seu código pode freqüentemente se por com o que está em seu FPU.

Apenas por diversão, eu joguei com a representação de carros alegóricos, seguindo as definições do C99 Padrão e eu escrevi o código abaixo.

O código imprime a representação binária dos flutuadores em 3 grupos separados

SIGN EXPONENT FRACTION

e depois que ele imprime uma soma, que, quando somados com precisão suficiente, ele irá mostrar o valor que realmente existe no hardware.

Assim, quando você escreve float x = 999..., o compilador irá transformar esse número em uma representação pouco impresso pela função xx tal que a soma impresso pelo yy função de ser igual ao número dado.

Na realidade, esta soma é apenas uma aproximação. Para obter o número 999.999.999 o compilador irá inserir em representação do flutuador pouco o número 1000000000

Depois do código que eu anexar uma sessão de console, em que eu calcular a soma dos termos para ambas as constantes (menos PI e 999999999) que realmente existe no hardware, inserida há pelo compilador.

#include <stdio.h>
#include <limits.h>

void
xx(float *x)
{
    unsigned char i = sizeof(*x)*CHAR_BIT-1;
    do {
        switch (i) {
        case 31:
             printf("sign:");
             break;
        case 30:
             printf("exponent:");
             break;
        case 23:
             printf("fraction:");
             break;

        }
        char b=(*(unsigned long long*)x&((unsigned long long)1<<i))!=0;
        printf("%d ", b);
    } while (i--);
    printf("\n");
}

void
yy(float a)
{
    int sign=!(*(unsigned long long*)&a&((unsigned long long)1<<31));
    int fraction = ((1<<23)-1)&(*(int*)&a);
    int exponent = (255&((*(int*)&a)>>23))-127;

    printf(sign?"positive" " ( 1+":"negative" " ( 1+");
    unsigned int i = 1<<22;
    unsigned int j = 1;
    do {
        char b=(fraction&i)!=0;
        b&&(printf("1/(%d) %c", 1<<j, (fraction&(i-1))?'+':')' ), 0);
    } while (j++, i>>=1);

    printf("*2^%d", exponent);
    printf("\n");
}

void
main()
{
    float x=-3.14;
    float y=999999999;
    printf("%lu\n", sizeof(x));
    xx(&x);
    xx(&y);
    yy(x);
    yy(y);
}

Aqui está uma sessão de console em que eu calcular o valor real do flutuador que existe no hardware. Eu costumava bc para imprimir a soma dos termos outputted pelo programa principal. Pode-se inserir essa soma em repl python ou algo semelhante também.

-- .../terra1/stub
@ qemacs f.c
-- .../terra1/stub
@ gcc f.c
-- .../terra1/stub
@ ./a.out
sign:1 exponent:1 0 0 0 0 0 0 fraction:0 1 0 0 1 0 0 0 1 1 1 1 0 1 0 1 1 1 0 0 0 0 1 1
sign:0 exponent:1 0 0 1 1 1 0 fraction:0 1 1 0 1 1 1 0 0 1 1 0 1 0 1 1 0 0 1 0 1 0 0 0
negative ( 1+1/(2) +1/(16) +1/(256) +1/(512) +1/(1024) +1/(2048) +1/(8192) +1/(32768) +1/(65536) +1/(131072) +1/(4194304) +1/(8388608) )*2^1
positive ( 1+1/(2) +1/(4) +1/(16) +1/(32) +1/(64) +1/(512) +1/(1024) +1/(4096) +1/(16384) +1/(32768) +1/(262144) +1/(1048576) )*2^29
-- .../terra1/stub
@ bc
scale=15
( 1+1/(2) +1/(4) +1/(16) +1/(32) +1/(64) +1/(512) +1/(1024) +1/(4096) +1/(16384) +1/(32768) +1/(262144) +1/(1048576) )*2^29
999999999.999999446351872

É isso. O valor de 999999999 é de fato

999999999.999999446351872

Você também pode verificar com bc que -3,14 também é perturbado. Não se esqueça de definir um fator de scale em bc.

A soma exibido é o que dentro do hardware. O valor que você obter através do cálculo depende da escala definida. Eu fiz definir o fator scale a 15. Matematicamente, com precisão infinita, parece que é 1.000.000.000.

Outra maneira de olhar para este: Usado são 64 bits para representar números. Como consequência, não existe nenhuma maneira mais do que 2 ** 64 = 18,446,744,073,709,551,616 diferentes números pode ser representado com precisão.

No entanto, Math diz há já infinitamente muitas casas decimais entre 0 e 1. IEE 754 define uma codificação para usar esses 64 bits de forma eficiente para um espaço muito maior número acrescido de NaN e +/- infinito, de modo que existem folgas entre representado com precisão números cheias com apenas números aproximados.

Infelizmente 0,3 senta em uma lacuna.

Uma vez que esta discussão ramificou-se um pouco em uma discussão geral sobre atuais implementações de ponto flutuante eu acrescentaria que existem projectos em corrigir seus problemas.

Dê uma olhada https://posithub.org/ por exemplo, que mostra um tipo de número chamado posit (e seu predecessor unum), que promete oferecer uma melhor precisão com menos bits. Se o meu entendimento é correto, ele também corrige o tipo de problemas em questão. Muito interessante projecto, a pessoa por trás dele é um matemático que Dr. John Gustafson . A coisa toda é de código aberto, com muitas implementações reais em C / C ++, Python, Julia e C # ( https: // hastlayer. com / arithmetics ).

Imagine trabalhar em cada dez base com, digamos, 8 dígitos de precisão. Você verificar se

1/3 + 2 / 3 == 1

e saber que este retorna false. Por quê? Bem, como números reais temos

1/3 = 0,333 .... e 2/3 = 0,666 ....

Truncando em oito casas decimais, temos

0.33333333 + 0.66666666 = 0.99999999

que é, obviamente, diferente de 1.00000000 por exatamente 0.00000001.


A situação para os números binários com um número fixo de bits é exatamente análogo. Como números reais, temos

1/10 = ,0001100110011001100 ... (base 2)

e

1/5 = ,0011001100110011001 ... (base 2)

Se truncado estes para, digamos, sete bits, então teríamos

0.0001100 + 0.0011001 = 0.0100101

enquanto por outro lado,

3/10 = ,01001100110011 ... (base 2)

que, truncado para sete bits, é 0.0100110, e estes diferem por exatamente 0.0000001.


A situação exata é um pouco mais sutil, porque esses números são normalmente armazenados em notação científica. Assim, por exemplo, em vez de armazenar 1/10 como 0.0001100 podemos armazená-lo como algo como 1.10011 * 2^-4, dependendo de quantos bits que já alocados para o expoente e mantissa. Isso afeta quantos dígitos de precisão que você começa para seus cálculos.

O resultado é que por causa desses erros de arredondamento essencialmente você nunca quer usar == em números de ponto flutuante. Em vez disso, você pode verificar se o valor absoluto da sua diferença é menor do que alguns pequeno número fixo.

Desde Python 3.5 você pode usar a função math.isclose() para testar a igualdade aproximada :

>>> import math
>>> math.isclose(0.1 + 0.2, 0.3)
True
>>> 0.1 + 0.2 == 0.3
False

Math.sum (javascript) .... tipo de substituição operador

.1 + .0001 + -.1 --> 0.00010000000000000286
Math.sum(.1 , .0001, -.1) --> 0.0001

Object.defineProperties(Math, {
    sign: {
        value: function (x) {
            return x ? x < 0 ? -1 : 1 : 0;
            }
        },
    precision: {
        value: function (value, precision, type) {
            var v = parseFloat(value), 
                p = Math.max(precision, 0) || 0, 
                t = type || 'round';
            return (Math[t](v * Math.pow(10, p)) / Math.pow(10, p)).toFixed(p);
        }
    },
    scientific_to_num: {  // this is from https://gist.github.com/jiggzson
        value: function (num) {
            //if the number is in scientific notation remove it
            if (/e/i.test(num)) {
                var zero = '0',
                        parts = String(num).toLowerCase().split('e'), //split into coeff and exponent
                        e = parts.pop(), //store the exponential part
                        l = Math.abs(e), //get the number of zeros
                        sign = e / l,
                        coeff_array = parts[0].split('.');
                if (sign === -1) {
                    num = zero + '.' + new Array(l).join(zero) + coeff_array.join('');
                } else {
                    var dec = coeff_array[1];
                    if (dec)
                        l = l - dec.length;
                    num = coeff_array.join('') + new Array(l + 1).join(zero);
                }
            }
            return num;
         }
     }
    get_precision: {
        value: function (number) {
            var arr = Math.scientific_to_num((number + "")).split(".");
            return arr[1] ? arr[1].length : 0;
        }
    },
    diff:{
        value: function(A,B){
            var prec = this.max(this.get_precision(A),this.get_precision(B));
            return +this.precision(A-B,prec);
        }
    },
    sum: {
        value: function () {
            var prec = 0, sum = 0;
            for (var i = 0; i < arguments.length; i++) {
                prec = this.max(prec, this.get_precision(arguments[i]));
                sum += +arguments[i]; // force float to convert strings to number
            }
            return Math.precision(sum, prec);
        }
    }
});

a idéia é usar a matemática em vez operadores para evitar erros flutuador

Math.diff(0.2, 0.11) == 0.09 // true
0.2 - 0.11 == 0.09 // false

Também note que Math.diff e Math.sum auto-detectar a precisão para uso

Math.sum aceita qualquer número de argumentos

É realmente muito simples. Quando você tem um sistema de base 10 (como o nosso), só pode expressar frações que usam um fator primordial da base. Os factores primos de 10 são 2 e 5. Assim, 1/2, 1/4, 1/5, 1/8, e 1/10 podem todos ser expressos de forma limpa porque os denominadores todos os factores primos uso de 10. Em contraste, uma / 3, 1/6 e 1/7 são todas as casas decimais repetindo porque seus denominadores usar um fator primordial de 3 ou 7. Em binário (ou base 2), o único fator principal é 2. Assim, você só pode expressar frações limpa que contêm apenas 2 como um fator primordial. Em binário, 1/2, 1/4, 1/8 seriam todos expressa de forma limpa como decimais. Enquanto, 1/5 ou 1/10 seria dízima periódica. Então, 0,1 e 0,2 (1/10 e 1/5), enquanto decimais limpas em um sistema de base 10, são dízima periódica no sistema de base 2 o computador está operando. Quando você fazer matemática sobre essas casas decimais repetindo, você acabar com sobras que transitar quando você converter o número do computador base 2 (binário) em uma base legível mais humano 10 número.

A partir https://0.30000000000000004.com/

Uma questão diferente tem sido apontado como uma duplicata a esta:

Em C ++, porque é o resultado de diferentes cout << x do valor que um depurador está mostrando para x?

O x na questão é uma variável float.

Um exemplo seria

float x = 9.9F;

O 9.89999962 mostra depurador, a saída da operação cout é 9.9.

A resposta acaba por ser precisão padrão desse cout para float é de 6, por isso arredonda para um dígito 6 decimais.

aqui para referência

Esta foi realmente concebido como uma resposta para esta questão - que foi fechada como uma duplicata de < strong> este pergunta, enquanto eu unia esta resposta, então agora eu não posso postá-lo lá ... por isso vou postar aqui em vez!


resumo Pergunta:

No 10^-8/1000 planilha e 10^-11 avaliar como Igual , enquanto em VBA que não.

Na planilha, os números estão padronizando a notação científica.

Se você alterar as células para um formato de número ( Ctrl + 1 ) de Number com pontos 15 decimais, você obtém:

=10^-11 returns 0.000000000010000
=10^(-8/1000) returns 0.981747943019984

Assim, eles definitivamente não são a mesma coisa ... é praticamente zero e o outro apenas cerca de 1.

Excel não foi projetado para lidar com extremamente números pequenos - pelo menos não com instalar o estoque. Há suplementos para ajudar a melhorar a precisão número.


Excel foi projetado de acordo com a norma IEEE para Binary Floating-Point Arithmetic ( IEEE 754 ) . O padrão define como números de ponto flutuante são armazenados e calculados. O IEEE 754 padrão é amplamente utilizada porque permite-números de ponto flutuante de ser armazenados em uma quantidade razoável de espaço e cálculos podem ocorrer de forma relativamente rápida.

A vantagem de que flutua sobre a representação ponto fixo é que ele pode suportar uma ampla gama de valores. Por exemplo, uma representação de ponto fixo que tem 5 dígitos decimais com o ponto decimal posicionado após o terceiro dígito pode representar o número 123.34, 12.23, 2.45, etc. enquanto que a representação de ponto flutuante com precisão 5 dígitos pode representar 1,2345, 12345, 0,00012345 , etc. do mesmo modo, a representação de ponto flutuante também permite cálculos mais de uma vasta gama de magnitudes, mantendo precisão. Por exemplo,

img


Outras Referências:

frações decimais como 0.1, 0.2 e 0.3 não estão representados exatamente em binário codificado tipos de ponto flutuante. A soma das aproximações para 0.1 e 0.2 difere da aproximação utilizado para 0.3, daí a falsidade de 0.1 + 0.2 == 0.3 como pode ser visto mais claramente aqui:

#include <stdio.h>

int main() {
    printf("0.1 + 0.2 == 0.3 is %s\n", 0.1 + 0.2 == 0.3 ? "true" : "false");
    printf("0.1 is %.23f\n", 0.1);
    printf("0.2 is %.23f\n", 0.2);
    printf("0.1 + 0.2 is %.23f\n", 0.1 + 0.2);
    printf("0.3 is %.23f\n", 0.3);
    printf("0.3 - (0.1 + 0.2) is %g\n", 0.3 - (0.1 + 0.2));
    return 0;
}

Output:

0.1 + 0.2 == 0.3 is false
0.1 is 0.10000000000000000555112
0.2 is 0.20000000000000001110223
0.1 + 0.2 is 0.30000000000000004440892
0.3 is 0.29999999999999998889777
0.3 - (0.1 + 0.2) is -5.55112e-17

Para estes cálculos para ser avaliada de forma mais confiável, você precisará usar uma representação com base decimal para valores de ponto flutuante. faz o C Norma não especifica esses tipos por padrão, mas como uma extensão descrita em um Relatório Técnico . Tipos _Decimal32, _Decimal64 e _Decimal128 pode estar disponível no seu sistema (por exemplo gcc apoia-los em alvos selecionados , mas clang não apoiá-los no OS / X).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top