[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: expression evaluation problem
From: |
L A Walsh |
Subject: |
Re: expression evaluation problem |
Date: |
Thu, 25 Jul 2019 10:29:08 -0700 |
User-agent: |
Thunderbird |
On 2019/07/24 11:49, Greg Wooledge wrote:
> On Wed, Jul 24, 2019 at 11:43:11AM -0700, L A Walsh wrote:
>
>> Those aren't my variables.
>> If you assign the integer attribute to a variable it isn't the same
>> as when you don't.
>>
>
> In this case it *is*, because everything is being fed to an arithmetic
> command anyway.
>
> Simplifying the bug report as much as possible lets us avoid
> confusing and unnecessary diversions.
>
It might, but also can make the example less instructive and less
general. As the double paren's change the output, maybe I should use
quotes again:
str='cf80' v=960 uxtra=1 c=0
v="$v|($uxtra>=++$c?((0x${str:2*$c:2})&63)<<(6*($uxtra-$c)):0)"
echo $v
960|(1>=++0?((0xcf)&63)<<(6*(1-0)):0)
If I put back the original definitions of everything but 'str' being
and integer and then try it with the quotes:
str='cf80' int v=960 uxtra=1 c=0
v="$v|($uxtra>=++$c?((0x${str:2*$c:2})&63)<<(6*($uxtra-$c)):0)"
echo $v
960
So leaving the integer attributes and putting the whole right side
in might be a way to avoid bash reordering expression evaluation (?).
>
>>> The ${str:2*c:2} part is performed first, while c is still 0, and yet it
>>> expands to "cf".
>> ---
>> Why? It isn't even necessary when 'c' is greater than 'uxtra'
>>
>
> Because that's how bash works.
>
----
That doesn't explain why.
> $((expression))
>
> The expression is treated as if it were within double quotes,
---
I didn't use $((...)), I'd put the whole thing in ((...)) which I hoped
would treat the whole expression consistently.
>> Have you considered performing your calculation in steps, with
>> intermediate values stored in temporary variables with clear names?
>> That greatly improves readability.
>>
---
I compared my final form with one that was broken into intermediate
steps:
My core calculation (in 1 step -- not ideal for maintenance, but it was
a proof of concept for me):
(( (v=(v & byte_masks[uxtra]) << 6*uxtra
| (uxtra>=++c ? (63&sv[c]) << 6*(uxtra-c)
| (uxtra>=++c ? (63&sv[c]) << 6*(uxtra-c)
| (uxtra>=++c ? (63&sv[c]) << 6*(uxtra-c)
| (uxtra>=++c ? (63&sv[c]) << 6*(uxtra-c)
| (uxtra>=++c ? (63&sv[c]) << 6*(uxtra-c)
: 0 )
: 0 )
: 0 )
: 0 )
: 0 )
)
))
----
The one broken into steps looked like:
int s1=0 s2=0 s3=0 s4=0 s5=0 s6=0
int s1=$(($v & byte_masks[uxtra] << 6*uxtra))
c+=1
if ((uxtra>=c)); then
m2=$(((63&sv[c])))
s2=$((m2 << 6*(uxtra-c)))
c+=1
if ((uxtra>=c)); then
m3=$(((63&sv[c])))
s3=$((m2 << 6*(uxtra-c)))
c+=1
if ((uxtra>=c)); then
m4=$(((63&sv[c])))
s4=$((m2 << 6*(uxtra-c)))
s4=$(((63&sv[c]) << 6*(uxtra-c)))
c+=1
if ((uxtra>=c)); then
m5=$(((63&sv[c])))
s5=$((m2 << 6*(uxtra-c)))
c+=1
if ((uxtra>=c)); then
m6=$(((63&sv[c])))
s6=$((m2 << 6*(uxtra-c)))
fi
fi
fi
fi
fi
v=$((s1|s2|s3|s4|s5|s6))
----
I know it did expand the variable names, but I wanted to get
a quick idea of timing differences/overhead.
Timing it to decode a 4-hex numbers showed the split form
to take a bit over 25% longer.
>
>> Isolating the ++c into its own step would also remove all questions
>> about whether the increment is performed before or after other
>> calculations (or in this case, parameter expansions). In-lining ++c
>> inside a larger calculation can be OK in very simple situations, but
>> a nightmare to read/understand/debug in more complex cases.
>>
I've had programs like the 1st one above, where to debug it I rewrote it
into an if/then/else structure for debugging. Since it wasn't a time
critical routine, I just kept it in the expanded form as it was (is) easier
to maintain and modify.
Note while you removed the 'int' attribute on the vars, the working
form needed them to produce a correct result.
To fix the code, I changed the string into an array which seems
to get computed in expected order".