0

Here is the example:

set /a "number1=1"
set /a "number2=10"
if %number1% LSS %number2% (set /a "number1=%number1%+1")
echo result=%number1%

This code displays:

result=2

Is it possible in batch file to change the if statement to something like:

set /a "number1=%number1%+(%number1% LSS %number2%)"

?

The changed code at this moment displays:

Unbalanced parenthesis.
result=1
Lex
  • 444
  • 1
  • 3
  • 12
  • 1
    You are restricted to using the options that are defined in the help file for the `SET` command. `LSS` is not an option. Open up a command prompt and type: `set /?` to read about the usage. – Squashman Sep 21 '20 at 20:45
  • So it seems that I can't create any direct math expression with the output based on compare statement. Thank you for the answer, @Squashman. – Lex Sep 21 '20 at 20:59
  • Do not use an __arithmetic expression__ (the string after `set /a` to define an environment variable. That is possible, but cause unnecessary execution of lots of CPU instructions in comparison to `set "number1=1"` and `set "number2=10"`. The reasons are described in detail in my answer on [Why is no string output with 'echo %var%' after using 'set var = text' on command line?](https://stackoverflow.com/a/26388460/3074564) `set /A number1+=1` is also much more efficient than `set /a "number1=%number1%+1"` and is described by help of `set` output on running `set /?` in a command prompt window. – Mofi Sep 22 '20 at 05:22
  • @Mofi: Really? I always thought that use `set /a` to define numerical variable will be much better / faster / more optimized. :o Thank you so much for this insight! I know about `+=` thing but I copied my example from some code and just (to make it easier) renamed the variables (these two `number1` were two different variables in original code). :) – Lex Sep 23 '20 at 16:33

2 Answers2

1

The set /A command does not feature comparison operators.

However, there is a way to compare two numbers and to get a value of -1 if the first number is less than the second one. For this, we need to know that the logical right-shift operator >> actually performs an arithmetic shift, meaning that the value of the most significant bit, which represents the sign, is shifted in at the left, hence the current sign of the number is maintained.

So for example:

  • the number +10 (binary representation 00000000 00000000 00000000 00001010) shifted to the right by one bit becomes +5 (binary representation 00000000 00000000 00000000 00000101);
  • the number -10 (binary representation 11111111 11111111 11111111 11110110) shifted to the right by one bit becomes -5 (binary representation 11111111 11111111 11111111 11111011);

When we shift to the right by 31 (or more) bit positions, the result is 32 times the sign bit (as we have 32-bit signed integers), which results in a value of -1 for negative input values and 0 for others.

When we now apply this technique, it leads us to the following code:

set /A "number1=%number1%-((%number1%-%number2%)>>31)"

You may omit the surrounding %-signs since set /A interprets any character strings that do not represent numbers or operators as variable names anyway:

set /A "number1=number1-((number1-number2)>>31)"

This can be further simplified using the appropriate assignment operator:

set /A "number1-=(number1-number2)>>31"
aschipfl
  • 33,626
  • 12
  • 54
  • 99
  • Thank you for your answer and it's good to know about `>>`. The bad news are that my example displayed only one specific case where I was going to use this method - I wanted to use it as "general one" (for example: to compare strings as well). – Lex Sep 21 '20 at 23:03
  • You're welcome! Well, I don't know if I get that correctly, but `set /A` won't help for string comparisons. What exactly do you "generally" want to compare? – aschipfl Sep 21 '20 at 23:05
  • I have some `if` statements just to set variables based on the `if` result. The more advanced example I wanted to use would look something like `set /a "result=%result%+100*("%string%"=="hello")+100*(%number% GTR 100)"` - that would add values of 100 to `result` variable per each "true" `if`. – Lex Sep 21 '20 at 23:15
  • I see, but the string comparison expression `"%string%"=="hello"` cannot be evaluated in a `set /A` expression, you have to use `if` for this… – aschipfl Sep 21 '20 at 23:36
  • Yes, now I know that. Such command syntax I tried to implement here exists in other programming languages I used so I thought that maybe something similar can be used also in batches, but it seems batches use "less advanced" language in that. Still, thank you so much for the knowledge about `>>`! Batches use a lot of such "simple symbols" to create "more advanced things" and I still do not know all of them. – Lex Sep 22 '20 at 04:22
0

So your looking for something like a function or macro you can pass the values to for assessment?

The below is a macro demonstrating the concept. It is not a complete solution, as strings should be padded so they of equal length prior to assesment

@Echo Off
 Set "Compare=Set "$RV="&For %%n in (1 2)Do if %%n==2 (For /F "Tokens=1,2 Delims={}" %%G in ("!Strings!")Do ((If "%%~G" LSS "%%~H" (Set "$RV=LSS") Else If "%%~G" EQU "%%~H" (Set "$RV=EQU") Else If "%%~G" GTR "%%~H" (Set "$RV=GTR"))&Echo/%%~G !$RV! %%~H))Else Set Strings="
 Setlocal EnableDelayedExpansion
rem // example usage
 %Compare:$RV=Ex[1]%{one}{oneb}
 %Compare:$RV=Ex[2]%{one}{one}
 %Compare:$RV=Ex[3]%{oneb}{one}
 %Compare:$RV=Ex[4]%{13}{02}
 %Compare:$RV=Ex[5]%{02}{13}
Echo/rem // Integers need to be 0 prefixed and strings should be padded to equal length or will return false:
 %Compare:$RV=Ex[6]%{2}{13}
 %Compare:$RV=Ex[7]%{two}{oneplus}
 Set Ex[

Below is the fully realised compare macro that Pads strings to prevent false results when string lengths differ. The macro is defined using the \n newline syntax for the sake of readablitiy, however the compound version is also included.

@Echo Off & Setlocal DISABLEdelayedexpansion
rem // Pads string with 125 characters to prevent false results when string lengths differ. If comparing strings exceeding 125 characters in length,
rem // increase length of the padding and adjust s1 and s2 Substring modification length to match the number of padding characters.
 Set "PAD=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
(Set \n=^^^
%= Newline Variable. Do not Modify. =%
)
 Set Compare=Set "$RV=" ^& For %%n in (1 2)Do if %%n==2 (%\n%
  For /F "Tokens=1,2 Delims={}" %%G in ("!Strings!")Do (%\n%
   Set "s1=!PAD!%%~G" ^& Set "s1=!s1:~-125!" ^& Set "s2=!PAD!%%~H" ^& Set "s2=!s2:~-125!"%\n%
   If "!s1!" LSS "!s2!" (Set "$RV=LSS")%\n%
   If "!s1!" EQU "!s2!" (Set "$RV=EQU")%\n%
   If "!s1!" GTR "!s2!" (Set "$RV=GTR")%\n%
   Echo/%%~G !$RV! %%~H%\n%
  )%\n%
 )Else Set Strings=
 Setlocal ENABLEdelayedexpansion
rem // example usages
For %%A in (one two three)Do for %%B in (three two one)Do  %Compare:$RV=Ex[0]%{%%A}{%%B}
 %Compare:$RV=Ex[1]%{one}{oneb}
 %Compare:$RV=Ex[2]%{one}{one}
 %Compare:$RV=Ex[3]%{oneb}{one}
 %Compare:$RV=Ex[4]%{13}{2}
 %Compare:$RV=Ex[5]%{2}{13}
 Set Ex[
Endlocal & Endlocal & Goto :Eof
rem // compound version of Compare Macro:
 Set "Compare=Set "$RV="&For %%n in (1 2)Do if %%n==2 (For /F "Tokens=1,2 Delims={}" %%G in ("!Strings!")Do (Set "s1=!PAD!%%~G"&Set "s2=!PAD!%%~H"&Set "s1=!s1:~-125!"&Set "s2=!s2:~-125!"&(If "!s1!" LSS "!s2!" (Set "$RV=LSS") Else If "!s1!" EQU "!s2!" (Set "$RV=EQU") Else If "!s1!" GTR "!s2!" (Set "$RV=GTR"))&Echo/%%~G !$RV! %%~H&Set "s1="&Set "s2=")) Else Set Strings="
  • Compare macro is case sensitive. Change the If conditions within the macro to include the /I switch if you require case to be ignored

To use the macro in the Desired context:

 (%compare%{String 1}{String 2}) > Nul & If "!$RV!"=="LSS" (Echo/true)& Rem // replace Echo command within Parantheses with desired command
  • with Strings 1 and 2 being stand ins for whatever variables or For loop tokens you're comparing.
T3RR0R
  • 2,747
  • 3
  • 10
  • 25
  • Well, I did not try to find a function / macro - I tried to find the "proper syntax" to use the expression I know from other programming languages, but it seems language used in batches is "less advanced". I think that for the clarity of my code I'll go back to use of `if`. Thank you so much for your effort! – Lex Sep 22 '20 at 04:28
  • The above is a compounded macro, with all relevent commands concatenated on the same line. Using the `\n` syntax for macro definition [developed here](https://www.dostips.com/forum/viewtopic.php?f=3&t=1827&p=7346&hilit=macro+arguments#p7346), commands used in the macro can be appended to the definition in a manner that greatly improves readability. Alternately a function can scripted at a Label to perfom the same task of repetitive handling of dynamic datasets and accessed using the Call command – T3RR0R Sep 22 '20 at 14:41
  • Note regarding the usage of the macro: $RV is the defined name of the return variable that indicates LSS EQU or GTR status of the string. in the usage examples, $RV is replaced using substring modification - an optional feature that allows for tracking / additional comparison. It's easy enough to modify the macro to perform other actions depending on the LSS EQU GTR status of the string command - simply replace the Set $Rv command in the relevant if statement with the commands your require. – T3RR0R Sep 22 '20 at 15:46