1
votes

I have input file with with x1 , x2 and x values, I want to check if x is midpoint between x1 and x2. But the comparison is failing.

sample input file

x1=20.9280 x2=20.9600 x=20.9440
x1=20.9280 x2=20.9600 x=20.9440
x1=22.7840 x2=22.8160 x=22.8000

Awk command

awk -F'[ =]' '{ if(($2 + $4)/2 != ($6)) print ($2 + $4)/2, " ", $6;}' sample

OUTPUT

20.944   20.9440
20.944   20.9440
22.8   22.8000

Comparison is failing due to extra zeros after decimal points. Please help to fix it.

3
you could remove 0 at the end of $6 with sub(/0+$/, "", $6) before performing your comparisons.. but in general, comparing floats directly is risky, typically absolute difference between the two floats is checked against a small value, like 0.00001 - Sundeep
@EdMorton I think OP wanted to print cases where the last column doesn't match midpoint calculation, for example x1=20.9280 x2=20.9600 x=20.9240 (changed example) .. but with the code posted in question, all three lines were printed even though all of them have correct midpoint in final column.. that's why OP says they expected no output for example lines posted in question... - Sundeep
@EdMorton so, OP is probably just looking for awk -F'[ =]' '{gsub(/\./,"")} ($6*2) != ($2+$4)' - Sundeep
@Sundeep thanks, I'm an idiot, I missed the ! in the comparison. And yes, that conversion is what I put in my answer. - Ed Morton
well, I copied it from your answer ;) nice trick btw! - Sundeep

3 Answers

1
votes

This is happening due to floating point comparison issue commonly found in all platforms.

You may use this awk for floating point number comparison by converting number to a floating point with 4 decimal points:

awk -F'[ =]+' '{avg = sprintf("%.4f", ($2 + $4) / 2)} avg != $6 { print avg, $6 }' file

If you have gnu awk then you can set precision to a lower number:

awk -M -v PREC=30 -F'[ =]+' '{avg = ($2 + $4) / 2; $6 += 0} avg != $6 { print avg, $6 }' file
1
votes

Not really an anwser but do demonstrate. You are comparing floating point numbers, they are not equal. I replaced print with printf and modifiers with enough decimals (20, %.20f):

$ awk -F'[ =]' '{
    if(($2 + $4)/2 != ($6)) 
        printf "%.20f %.20f\n", ($2 + $4)/2, $6
}' file

Ottput:

20.94400000000000261480 20.94399999999999906208
20.94400000000000261480 20.94399999999999906208
22.79999999999999715783 22.80000000000000071054

So use sprintf and appropriate modifiers (see the printf I used) to control the values.

0
votes

As others have pointed out, if you are having a problem then it's probably that you're just tripping over the common floating point arithmetic issue but since all of your input values have the same precision you can just get rid of the .s to treat the input numbers as integers and multiply by 2 instead of dividing by 2 just to keep it an integer comparison too:

$ awk -F'[ =]' '{o=$0; gsub(/\./,"")} ($6*2) == ($2+$4){$0=o; print ($2+$4)/2, $6}' file
20.944 20.9440
20.944 20.9440
22.8 22.8000

$ awk -F'[ =]' '{o=$0; gsub(/\./,"")} ($6*2) != ($2+$4){$0=o; print ($2+$4)/2, $6}' file
$