Path: csiph.com!xmission!news.snarked.org!news.linkpendium.com!news.linkpendium.com!panix!usenet.stanford.edu!not-for-mail From: L A Walsh Newsgroups: gnu.bash.bug Subject: Re: expression evaluation problem Date: Thu, 25 Jul 2019 10:29:08 -0700 Lines: 140 Approved: bug-bash@gnu.org Message-ID: References: <5D3889D2.3090101@tlinx.org> <20190724175103.GN1218@eeg.ccf.org> <5D38A6BF.9020309@tlinx.org> <20190724184943.GO1218@eeg.ccf.org> <5D39E6E4.8030902@tlinx.org> NNTP-Posting-Host: lists.gnu.org Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-Trace: usenet.stanford.edu 1564075760 5180 209.51.188.17 (25 Jul 2019 17:29:20 GMT) X-Complaints-To: action@cs.stanford.edu To: bug-bash Envelope-to: bug-bash@gnu.org User-Agent: Thunderbird In-Reply-To: <20190724184943.GO1218@eeg.ccf.org> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x (no timestamps) [generic] X-Received-From: 173.164.175.65 X-BeenThere: bug-bash@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Bug reports for the GNU Bourne Again SHell List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Mailman-Original-Message-ID: <5D39E6E4.8030902@tlinx.org> X-Mailman-Original-References: <5D3889D2.3090101@tlinx.org> <20190724175103.GN1218@eeg.ccf.org> <5D38A6BF.9020309@tlinx.org> <20190724184943.GO1218@eeg.ccf.org> Xref: csiph.com gnu.bash.bug:15245 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".