Operações de flutuação com bc não são precisas?

4

Quando estou realizando operações de flutuação no shell usando bc, o resultado não é o mesmo se eu usar uma calculadora comum. Estou fazendo algo errado?

Por exemplo, preciso encontrar um volume de uma esfera. O usuário insere o valor do raio.

pi = 3.14

volume=$(echo "scale = 2; (4 / 3) * $pi * ($r ^ 3)" | bc)
echo "Volume is $volume"

Se radius = 3, unix retorna 112.59 e a calculadora 113.1.

    
por Slado 02.05.2016 / 15:32

2 respostas

13

Você precisa entender o significado da escala de uma expressão em bc . bc pode fazer precisão arbitrária (o que não significa necessariamente precisão infinita), enquanto a sua calculadora provavelmente terá a precisão do tipo de dados float ou double do seu processador.

Em bc . A escala é o número do decimal após o ponto, portanto, relacionado à precisão. A escala de uma expressão é determinada com base em regras que dependem de qual operador está envolvido e da variável scale (essa variável é aquela que fornece a dimensão arbitrária do precisão de bc , isto é, que pode tornar sua precisão tão grande quanto você quiser).

Por exemplo, a escala do resultado de uma divisão é scale . Então 4/3 quando scale é 2 é 1.33 , então uma aproximação muito grosseira de 4/3 . A escala de x * y será min(a+b,max(scale,a,b)) (onde a é a escala de x e b da escala de y ), portanto, aqui 2 . então 1.33 * 3.14 será 4.17 .

Para as regras, você pode verificar a especificação POSIX para bc .

Se você quiser uma precisão maior, aumente scale . Você pode aumentá-lo indefinidamente. Com bc -l , scale é definido automaticamente como 20 .

$ pi='(a(1)*4)' r=3
$ $ echo "(4 / 3) * $pi * ($r ^ 3)" | bc -l
113.09733552923255658339

$ echo "scale=1000; (4 / 3) * $pi * ($r ^ 3)" | bc -l
113.0973355292325565846551617980621038310980983775038095550980053230\
81390626303523950609253712316214447357331114478163039295378405943820\
96034211293869262532022821022769726978675980014720642616237749375071\
94371951239736040606251233364163241939497632687292433484092445725499\
76355759335682169861368969085854085132237827361174295734753154661853\
14730175311724413325296040789909975753679476982929026989441793959006\
17331673453103113187002257495740245517842677306806456786589844246678\
87098096084205774588430168674012241047863639151096770218070228090538\
86527847499397329973941181834655436308584829346483609858475202045257\
72294881898002877683392804259302509384339728638724440983234852757850\
73357828522068813321247512718420036644790591105239053753290671891767\
15857867345960859999994142720979823815034238137946746942088054039248\
86988951308030971204086612694295227741563601129621951039171511955017\
31142218396089302929537125655435196874321744263099764736353375070480\
1468800991581641650380680694035580030527317911271523

$ echo "scale=1; (4 / 3) * $pi * ($r ^ 3)" | bc -l
97.2

Você também pode fazer todos os seus cálculos com um alto scale e reduzi-lo no final para exibição:

$ echo "scale=10; (4 / 3) * $pi * ($r ^ 3)" | bc -l
113.0973355107
$ echo "scale=100; x = (4 / 3) * $pi * ($r ^ 3); scale = 10; x / 1" | bc -l
113.0973355292
    
por 02.05.2016 / 15:56
0

Você deve usar um valor melhor para pi e permitir que a escala seja definida por padrão em bc (20 dígitos):

$ echo "r=3; pi=4*a(1); (4/3)*pi*(r^3)" | bc -l
113.09733552923255658339

Essa é a melhor maneira de realizar o cálculo. A escala é um parâmetro para definir a precisão usada no cálculo, não um parâmetro para formatar a saída numérica do resultado.

Para formatar o número que você pode usar printf:

$ result="$(echo "r=3; pi=4*a(1); (4/3)*pi*(r^3)" | bc -l)"
$ printf '%.2f' "$result"
113.10

Se você tiver problemas com o caractere decimal numérico ( , ou . ), você poderá usar esta versão:

$ LC_NUMERIC=C printf '%.2f' "$result"
113.10

printf arredonda o número após a implementação do IEEE 754

Claro, você pode forçar bc a cortar as casas decimais com uma mudança final na escala:

$ echo "r=3; pi=4*a(1); res=(4/3)*pi*(r^3); scale=2; res/1" | bc -l
113.09

Mas isso é uma má ideia em geral.

    
por 03.05.2016 / 15:01