Incomplete documentation for SET /A

Microsoft Windows
Post Reply
User avatar
MigrationUser
Posts: 336
Joined: 2021-Jul-12, 1:37 pm
Been thanked: 2 times
Contact:

Incomplete documentation for SET /A

Post by MigrationUser »

16 Jan 2013 01:37
npocmaka

Just compared the SET /? and SS64 page.

Nothing is mentioned about : ~ , ! (and in fact also for - as a unary operator) and , .As they are almost obvious the parentheses also are not mentioned as logical separators ..


~ is the most strange - It acts as -(x+1) :

Code: Select all

C:\>set /a "i=~5"
-6
C:\>set /a "i=~(-5)"
4
EDIT: according to this - http://sourcedaddy.com/windows-7/settin ... mmand.html - it's a bitwise invert (..). ! definitely does not look like bitwise not to me.

! is the most useless -In fact I don't know if it can do anything at all - it acts as every non-arithmetic symbol and used as a unary operator converts anything to 0 :

Code: Select all

C:\>set /a "i=s7"
0
C:\>set /a "i=!7"
0
EDIT: Of course there is (as always) an old DOSTIPS thread where dbenham explains everything - http://www.dostips.com/forum/viewtopic.php?f=3&t=2196
! can be used against zero.And help about << and >> seems to be not correct (check the example).

I have no idea why it's included in the help (I still hope for some uber-super-duper hidden feature).

, is the most fun (I have definitions for all of them :-) ) .With this a few arithmetic variables could be initialized or an arithmetic operation could be executed without being assigned to a variable:

Code: Select all

C:\>set /a "i=1,5+3"
8
C:\>set /a "i=1,2+2"
4
C:\>set /a "i=1,2+2,j=3+2"
5
C:\>echo %i% and %j%
1 and 5
C:\>set /a "2+2"
4
Also can break the parentheses context:

Code: Select all

C:\>set /a "i=1,(2,+23)+2"
25
Last edited by npocmaka (16 Jan 2013 11:46)

----------------------------

#2 16 Jan 2013 02:57
npocmaka


And three more pitfalls of set /a :

Code: Select all

C:\>set /a "a=1,b=1+%a%"
Missing operand.
with enabled delayed expansion does not produce an error but the value of b will be 1:

Code: Select all

@echo off
setlocal ENABLEDELAYEDEXPANSION
set /a "a=1,b=1+!a!"
echo !b!
endlocal
do not work and backward too :-)

here b will be 0 :

Code: Select all

@echo off
setlocal ENABLEDELAYEDEXPANSION
set /a "a=0"
set /a "b=!!a!"
echo !b!
endlocal
call set /a works but not in these cases:

Code: Select all

@echo off
setlocal 
set /a "a=5"
call set /a "b=6^%a%"
call set /a "b=6%%a%"
endlocal
----------------------------

#3 16 Jan 2013 05:58
carlos


npocmaka. I write that I learn in my experience with this command. The /A in set maybe means arithmetic, and means that the expression will be evaluated. Then:

Code: Select all

set "n=5"
is more faster than:

Code: Select all

set /a "n=5"
because is not neccesary evaluated the expression. Always the result is saved as text in the block of environment variables.
The ! (logical negation), for work with it is neccesary turn off delayed expansion. 0 is turned 1 and all other is turned 0.
The ~ (bitwise negation) is complement.
The - (sign negation) positive sign turn negative, and negative sign turn positive.

With extensions enabled you can use the other expansion:
Is not necessary turn on delayedexpansion.
You only use the name of the variable:

Code: Select all

Set "number=10"
Set /a "new=1+number"
Last edited by carlos (16 Jan 2013 12:20)

----------------------------

#4 16 Jan 2013 08:05
npocmaka
carlos wrote:

Code: Select all

    Set "number=10"
    Set /a "new=1+number"
this was unexpected but cool ...
Turns out that consecutive sets work after all:

Code: Select all

C:\>set /a "first=1,second=first+1"
2
works also with encabledelayedexpansion.

Last edited by npocmaka (16 Jan 2013 11:42)

----------------------------

#5 16 Jan 2013 12:14
carlos


This is a example of use of ! operator:

Code: Select all

@Echo Off
SetLocal Enableextensions
Call :isLeap 2008
Echo 2008 : leap : %errorlevel%
Call :isLeap 2000
Echo 2000 : leap : %errorlevel%
Call :isLeap 1900
Echo 1999 : leap : %errorlevel%
Pause
Goto :Eof

:isLeap
::Author: Carlos
::Return 0 or 1 if is leap year
::Argument: year
SetLocal EnableExtensions DisableDelayedexpansion
Set /A "ly=(!(%~1%%4)&!!(%~1%%100))|!(%~1%%400)"
Exit /B %ly%
Last edited by carlos (16 Jan 2013 12:16)

----------------------------

#6 16 Jan 2013 15:47
npocmaka
carlos wrote:

The ! (logical negation), for work with it is neccesary turn off delayed expansion. 0 is turned 1 and all other is turned 0.


Hmm.. Probably cmd.exe searches for a variable when expansion is on - and does not effect then.
carlos wrote:

With extensions enabled you can use the other expansion:
More interesting SET /A does not work with DISABLEEXTENSIONS at all :

Code: Select all

@echo off
setlocal 
setlocal 	ENABLEDELAYEDEXPANSION
 set /a "test1=!0"
 echo !test1!
 set /a "test1=!1"
 echo !test1!

endlocal
 set /a "test2=!0"
 echo %test2%
 set /a "test2=!1"
 echo %test2%

  setlocal DISABLEEXTENSIONS
	set /a "test5= 1 + 1"
         rem will not display **2**
	echo **%test5%**
  endlocal
 
 setlocal DISABLEEXTENSIONS
 rem this produce error
 set /a "test3=!0"
 echo %test3%
 set /a "test3=!1"
 echo %test3%

 
 endlocal
 
 setlocal DISABLEEXTENSIONS ENABLEDELAYEDEXPANSION
 rem this produce error too
 set /a "test4=!0"
 echo %test4%
 set /a "test4=!1"
 echo %test4%
 endlocal
 

endlocal
Last edited by npocmaka (16 Jan 2013 15:59)

----------------------------

#7 17 Jan 2013 02:17
Simon Sheppard


I've updated the SET page now, thanks for flagging this up npocmaka

----------------------------

#8 17 Jan 2013 03:20
Aacini


Comma operator allows to perform large calculations in just one line, like in this example:

Code: Select all

REM CALCULATE MONTH AND DAY OF THE EASTER SUNDAY OF THE GIVEN YEAR
SET YY=%1
SET /A A=YY%%19, B=YY/100, C=YY%%100, D=B/4, E=B%%4, F=(B+8)/25, G=(B-F+1)/3, H=(A*19+B-D-G+15)%%30, I=C/4, K=C%%4, L=((E+I)*2-H-K+32)%%7, M=(A+H*11+L*22)/451, N=H+L-M*7+114, MM=N/31, DD=N%%31+1
ECHO Easter Sunday of year %YY% is on month %MM% day %DD%
You can use the assignment operator(s) at any point inside the expression, but I don't like use this feature because the result looks weird and may cause doubts in the order of evaluation:

Code: Select all

SET /A D=(B=YY/100)/4, E=B%%4, G=(B-(B+8)/25+1)/3, H=((A=YY%%19)*19+B-D-G+15)%%30, K=(C=YY%%100)%%4, M=(A+H*11+(L=((E+C/4)*2-H-K+32)%%7)*22)/451, MM=(N=H+L-M*7+114)/31, DD=N%%31+1
The fact that SET /A command can process variable names may save you from achieve an additional value expansion when array elements are processed inside a FOR loop, like in this example:

Code: Select all

rem Copy N random numeric elements from Original to New vectors
for /L %%i in (1,1,%N%) do (
   set /A index=(!random!*upperLimit)/32768+1
   set /A New[!index!]=Original[!index!]
)
Note that you can not modify a variable and use its value as subscript for an array element in the same SET /A command; that is, this does NOT work:

Code: Select all

for /L %%i in (1,1,%N%) do (
   set /A index=(!random!*upperLimit)/32768+1, New[!index!]=Original[!index!]
)
However, you can combine a %normal% expansion, a !delayed! expansion and the variable name expansion of SET /A command to achieve a complex calculation in a simple way, as described in this example:

Code: Select all

    rem DRAWEQUATION.BAT - Draw simple equations y=f(x) in X-Y plane

    for /L %%x in (0,1,%maxCol%) do (
       set /A "y=(Top-(%f(x)%))/yStep"
    . . . .
    )
It is interesting to note that in the evaluation of previous equation, the substitution of variable values are performed three times in the same line:

0- Read the equation: f(x)=SIN[!x!]*20>>16
1- Normal %variable% expansion: set /A "y=%f(x)%" becomes: set /A "y=SIN[!x!]*20>>16"
2- Delayed !variable! expansion: set /A "y=SIN[!x!]*20>>16" becomes: set /A "y=SIN[1]*20>>16" (when x=1)
3- Replace variable values in SET /A command: set /A "y=1144*20>>16"
Exclamation-mark (logical NOT) operator is the key to achieve conditional expressions in a way equivalent to C's conditional operator (cond)?then:else, but just for certain particular cases. For example, next line check if SIZE variable value is in range from LEFT to RIGHT (emulating an AND operation):

Code: Select all

set /A "C1=1,A=left-size,C1=!((A+1)/A), C2=1,A=size-right,C2=!((A+1)/A), test=C1*C2"
if %test% equ 1 (
   echo Size variable is in range
) else (
   echo Size variable out of range
)
You may review full details on the use of this technique at this post.

Although SET /A documentation indicate that >> operator is right "logical shift" (RSH), it really operate as right "arithmetic shift" (RAS). If you need a pure logical shift, you must cancel the sign bit after the shift:

Code: Select all

C:>set /A var=-1
-1
C:>set /A "ras=var>>1"
-1
C:>set /A "rsh=var>>1&~(1<<31)"
2147483647
Last line is also an example on the use of bitwise AND (&) and bitwise NOT (~) operators.

Antonio

----------------------------

#9 28 Oct 2013 17:21
npocmaka


set /a outputs it result without new line at the end ? yikes

Code: Select all

C:\>set /a 1 & set /a 2
12
C:\>
----------------------------

#10 30 Oct 2013 22:56
carlos
npocmaka wrote:

Code: Select all

    set /a outputs it result without new line at the end ? yikes

    C:\>set /a 1 & set /a 2
    12
    C:\>
very interesting. that occurs in the interactive mode, almost in windows xp.

----------------------------

#11 03 Mar 2015 07:20
jumper


another handy feature is that it lets you assign multiple variables at once:

Code: Select all

set /a a=b=c=d=10
Post Reply