나는 한 줄에서 다음 줄로 일부 값을 전달하는 간단한 산술 연산을 수행하기 위해 awk를 얻으려고 노력해 왔습니다.
다음은 비교를 위한 최소 예제 쌍입니다. 첫 번째 예는 99.16 - 20.85 = 78.31 이후 예상되는 동작입니다.
$ echo -e "0,99.16\n20.85,78.31" | awk -F, '{
if (NR != 1 && (prior_tot - $1) != $2) {
print "Arithmetic fail..." $0
} else {
print "OK"
};
prior_tot = $2
}'
상품을 반품하다
OK
OK
두 번째 예는 99.15 - 20.85 = 78.30이므로 예상된 동작이 아닙니다.
$ echo -e "0,99.15\n20.85,78.30" | awk -F, '{
if (NR != 1 && (prior_tot - $1) != $2) {
print "Arithmetic fail..." $0
} else {
print "OK"
};
prior_tot = $2
}'
상품을 반품하다
OK
Arithmetic fail...20.85,78.30
무슨 일인지 설명해 줄 수 있는 사람 있나요?
답변1
부동 소수점 숫자 99.15, 28.85 및 78.30에는 정확한 IEEE 754 이진 표현이 없습니다. 동일한 계산을 수행하는 C 프로그램을 사용하여 이를 확인할 수 있습니다.
#include <stdio.h>
int
main(int ac, char **av)
{
float a = 99.15;
float b = 20.85;
float c;
printf("a = %.7f\n", a);
printf("b = %.7f\n", b);
c = a - b;
printf("c = %.7f\n", c);
return 0;
}
x86 및 x86_64 시스템에서 이러한 답변을 얻었습니다. 아마도 둘 다 이 작업을 수행하기 때문일 것입니다.IEEE 754부동 소수점 수학:
a = 99.1500015 b = 20.8500004 c = 78.3000031
일어나는 일은 다음과 같습니다. 부동 소수점 숫자는 부호 비트(양수 또는 음수), 다중 비트 및 지수로 표시됩니다. 모든 유리수(즉, 이 문서의 "부동 소수점 수")가 IEEE 754 형식으로 정확하게 표현될 수 있는 것은 아닙니다. 따라서 하드웨어는 최대한 가깝습니다. 불행하게도 귀하의 테스트 사례에서는 하드웨어가 이 세 가지 값을 정확하게 표현할 수 없습니다. double
대신 사용하더라도 그렇지 않을 것입니다. 아마도 그럴 float
것 awk
입니다.
이것은추가 설명정확한 이진 표현을 사용한 부동 소수점 숫자의 간격입니다.
일부 값은 테스트를 통과하지만 다른 값은 실패할 수 있습니다. 그렇지 않은 경우가 많습니다.
일반적으로 사람들은 다음을 수행하여 부동 소수점 문제를 해결합니다.
if (abs(c) <= epsilon) {
// We'll call it equal
} else {
// Not equal
}
에서 이 작업을 수행하는 것이 훨씬 더 어렵습니다 awk
. 화폐 단위와 두 개의 유효 숫자가 있는 하위 단위(예: 달러 및 센트)를 사용하여 화폐 계산을 수행하는 경우 모든 계산을 하위 단위(미국의 경우 센트)로 수행해야 합니다. 화폐 계산에 부동 소수점을 사용하지 마세요. 당신은 그 결정을 후회하게 될 것입니다.
답변2
부동 소수점 산술 문제가 있습니다.
$ awk 'BEGIN { printf "%.17f\n", 99.15-20.85 }'
78.30000000000001137
http://floating-point-gui.de/문제 해결에 도움이 될 수 있습니다. 부동 소수점이 무엇인지, 산술 오류가 발생하는 이유, 프로그램에서 이러한 문제를 방지하는 방법을 설명하려고 합니다.
답변3
숫자 형식을 지정하면 이러한 오류를 방지할 수 있습니다.
awk -F, '{
if (NR != 1 && sprintf(CONVFMT,prior_tot-$1) != $2)
{print "Arithmetic fail..." $0}
else
{print "OK"}
prior_tot = $2}'