Não posso fornecer uma boa solução para o problema, mas tentarei descrever em profundidade qual é o problema e fornecerei uma solução parcial.
O problema :
Os números de pontos flutuantes nas máquinas sofrem de precisão limitada: em suma, apenas um subconjunto limitado de números de ponto flutuante [por cada ordem de magnitude] é representável.
Os números de pontos flutuantes nas máquinas são representados de perto seguindo a notação normalizada ± significand * base ^ exponent
(onde base
= base de representação, significand
= qualquer número real > 0 e < = a base de representação e onde exponent
= ordem de magnitude): por exemplo, em uma máquina de 32 bits seguindo o padrão IEEE 754
, números de ponto flutuante de precisão simples são representados usando o primeiro bit para representar o sinal, os 8 bits a seguir para representar a ordem de magnitude e os últimos 23 bits para representar o significando, enquanto números de ponto flutuante de precisão dupla são representados usando o primeiro bit para representar o sinal, os 11 bits seguintes para representar a ordem de grandeza e os últimos 52 bits para representar o significando (a base , sendo sempre 2, não está representado). Para isso, o significando de um número deve ser representado sempre usando 23 bits (usando precisão simples) ou usando 52 bits (usando precisão dupla).
Uma propriedade dessa forma de representar números de ponto flutuante em um número fixo de bits é o número de significados representáveis por ordem de magnitude sempre igual, a "distância" média entre números de ponto flutuante representáveis com a mesma ordem de magnitude aumenta conforme a ordem de grandeza dos dois aumenta.
Para o acima, o primeiro problema é que, se o significante da notação normalizada de um número de ponto flutuante não estiver no conjunto limitado de significados representáveis, ele é arredondado para o significante mais próximo (maior ou menor) representável.
Falando de números representados com a mesma ordem de magnitude, um segundo problema é que, mesmo quando um número de ponto flutuante é representável com precisão, adicionar / subtrair outro número de ponto flutuante [precisamente representável] pode resultar em uma flutuação não representável. número do ponto, cujo significand será arredondado para o mais próximo (maior ou menor) significand representable.
Finalmente, falando de números representados com uma ordem de grandeza diferente, o terceiro problema (principalmente devido à arquitetura da CPU) é que, para poder realizar adições / subtrações entre números de ponto flutuante representados com uma ordem diferente de magnitude , os números precisam ser representados pela primeira vez usando a mesma ordem de grandeza; isto implica que a menor ordem de magnitude precisa ser aumentada, e que (para equilibrar isso) seu significado precisa ser deslocado para a direita, com a consequente perda do número de bits excedendo o 23/52 disponível; se isso não for suficiente, números de ponto flutuante com uma diferença significativa em sua ordem de grandeza podem resultar, uma vez adicionados / subtraídos, exatamente no número com o maior valor absoluto, isso para o problema já declarado (diferença insuficiente para pisar o não significados representáveis para cima / baixo para um significante representável superior / inferior diferente) e cada vez pior, à medida que a ordem de grandeza de dois números diverge mais.
As implicações de tudo isso são: você nunca terá certeza de obter um resultado preciso usando matemática de ponto flutuante, no entanto, isso pode ser mitigado usando uma representação de maior precisão.
A solução parcial :
Pelo acima, os resultados dessas awk
one-liners não são precisos; isso poderia ter sido mitigado pelo uso de precisão dupla em seus comandos printf
, mas isso não é suportado.
Isso diminuirá em 30
o valor dos três primeiros números separados por espaço em cada linha após a primeira linha corresponder a C
, mantendo o formato dos números. Como a versão awk
incluída no Ubuntu não suporta edições in-loco, você terá que usar awk
e redirecionar seu stdout
para um arquivo usando o operador bash
>
ou usar gawk
(GNU awk
) > = 4.10.0
;
Usando awk
:
awk 'NR==1, $0=="C"; $0=="C", 0 {if ($0!="C") printf "%.16f %.16f %.16f\n", $1-30, $2-30, $3-30}' data.txt > data_processed.txt
Usando gawk
(% GNUawk
) > = 4.10.0
gawk -i inplace 'NR==1, $0=="C"; $0=="C", 0 {if ($0!="C") printf "%.16f %.16f %.16f\n", $1-30, $2-30, $3-30}' data.txt
-
NR==1, $0=="C";
: seleciona e imprime todos os registros entre a primeira e a primeira correspondência C
inclusive;
-
$0=="C", 0 {if ($0!="C") printf "%.16f %.16f %.16f\n", $1-30, $2-30, $3-30}
: seleciona todos os registros entre o primeiro C
correspondente e o último inclusivo e imprime o primeiro, o segundo e o terceiro campo de cada registro selecionado não coincidentes C
do espaço duplo separado e diminuído por 30
mantendo o original formato do número;
Exemplo de saída:
~/tmp$ cat data.txt
TITLE
1.000000000000000
10.0000000000000000 0.0000000000000000 0.0000000000000000
0.0000000000000000 10.0000000000000000 0.0000000000000000
0.0000000000000000 0.0000000000000000 10.0000000000000000
U U
X X
C
0.2000000000000028 0.2000000000000028 0.2000000000000028
0.2967599999999990 0.0641000000000034 0.1551499999999990
0.1033699999999982 0.3361099999999979 0.244990000000001
~/tmp$ awk 'NR==1, $0=="C"; $0=="C", 0 {if ($0!="C") printf "%.16f %.16f %.16f\n", $1-30, $2-30, $3-30}' data.txt
TITLE
1.000000000000000
10.0000000000000000 0.0000000000000000 0.0000000000000000
0.0000000000000000 10.0000000000000000 0.0000000000000000
0.0000000000000000 0.0000000000000000 10.0000000000000000
U U
X X
C
-29.7999999999999972 -29.7999999999999972 -29.7999999999999972
-29.7032400000000010 -29.9358999999999966 -29.8448500000000010
-29.8966300000000018 -29.6638900000000021 -29.7550099999999986