So I have this program I'm trying to make:
//@echo off
@title Calculate Caloric Needs
set /p name=What is the name of the individual in question?
CLS
:START
set /p sex=Is %name% a M(ale) or F(emale)? (M/F)
CLS
if NOT %sex%==M if NOT %sex%==F (
echo Invalid Selection...Try Again....
goto START
)
set /p w=What is the target weight of %name%?
CLS
set /p h=What is the height of %name% IN INCHES?
CLS
set /p a=What is the age of %name%?
CLS
if %sex%==M set /a result=66 + (6.23 * %w%) + (12.7 * %h%) - (6.8 * %a%)
if %sex%==F set /a result=655+(4.35*%w%)+(4.7*%h%)-(4.7*%a%)
echo %result% is the caloric intake for %name%.
pause
exit
@echo off
has been disabled for the sake of troubleshooting.
It's a simple program meant to do some caloric intake calculations that are a bit arduous to do one by one.
Every time I reach the point where I need to do arithmetic calculations one of two things happens:
set /a
statement in an if %sex%==M ( CODE HERE )
block, the program will exit unexpectedly without showing an error message.Unbalanced Parenthesis
without any further explanation (thanks Windows...).As far as I know, mathematically speaking the parenthesis are completely balanced.
I've tried:
set /a
statement in both ""
and []
, set /a "result=66 + (6.23 * %w%) + (12.7 * %h%) - (6.8 * %a%)"
or set /a [result=66 + (6.23 * %w%) + (12.7 * %h%) - (6.8 * %a%)]
set /a result=66 + ^(6.23 * %w%^) + ^(12.7 * %h%^) - ^(6.8 * %a%^)
set /a result=66 + (6.23 * %w%)
. If I use set /a result=66 + (6.23 * %w%)
exactly, I can actually wrap the statement in an if ( CODE HERE )
block and have it actually return an error value. Although the error is still Unbalanced Parenthesis
...set /a result=66+(6.23*%w%)+(12.7*%h%)-(6.8*%a%)
VS. set /a result=66 + (6.23 * %w%) + (12.7 * %h%) - (6.8 * %a%)
So I am at a loss as to what could be causing this bizarre behavior.
The error message you are receiving is very confusing in your situation, because the parentheses in the set /A
command lines are balanced and work like that in case the entire command line is not placed inside of a parenthesised block of code on its own. Anyway, it is always a good idea to put the entire set /A
expression in between quotation marks, so neither parentheses nor other special characters can cause trouble.
Square brackets do not have any particular meaning for set /A
.
Anyway, the root cause of the error is the fact that you are using fractional numbers although set /A
only supports signed integer arithmetics in a 32-bit room (see the help of set /?
). Type set /A (1.2*3)
in a command prompt window and you will receive the same error message; but remove the .
and everything is going to be fine.
A possible work-around is to use something like fixed-point arithmetics, hence multiplying everything by a whole power of 10 during the calculations and dividing the result by the same power of 10 finally, or in other words, shifting the decimal point to the right before doing the calculations and shifting it back afterwards.
This is how it could look like (I also fixed some other issues in your code, but see below for that):
@echo off
@title Calculate Caloric Needs
cls
set "name=them"
set /P "name=What is the name of the individual in question? "
:START
set "sex="
set /P "sex=Is %name% a M(ale) or F(emale)? (M/F) "
if /I not "%sex%"=="M" if /I not "%sex%"=="F" (
echo Invalid Selection... Try Again...
goto :START
)
set "w=0"
set /P "w=What is the target weight of %name%? "
set "h=0"
set /P "h=What is the height of %name% IN INCHES? "
set "h=%h%." & rem // append a decimal dot to entry in `h`
set "mil=1%h:*.=%" & rem // store everything behind first dot in `mil`, prepend `1`
set /A "h+=0, mil+=0" & rem // convert `h` to integer, dismiss fractional part
set "mil=%mil%000" & rem // pad trailing zeros to `mil`
set "mil=%mil:~,4%" & rem // extract first four numerals from `mil`
set /A "mil+=5" & rem // add `5` to `mil` for rounding
if %mil:~,1% GTR 1 set /A "h+=1" & rem // regard carry of previous addition in `h`
set "h=%h%%mil:~-3,-1%" & rem /* append second and third numeral of `mil` to `h`,
rem hence dismissing previously prepended `1`;
rem so `h` holds the height in 100ths of inches now */
set "a=0"
set /P "a=What is the age of %name%? "
rem // quotation marks avoid trouble with parenthesis or other characters;
rem /* all original constants are multiplied by `1000` to avoid fractional parts,
rem except the factor at `h` which is multiplied by `10` only due to above
rem implicit multiplication of `h` by 100, then `500` is added for rounding,
rem and finally, the result is divided by `1000` to remove the previous factors: */
if /I "%sex%"=="M" (
set /A "result=(66000+(6230*%w%)+(127*%h%)-(6800*%a%)+500)/1000"
) else if /I "%sex%"=="F" (
set /A "result=(655000+(4350*%w%)+(47*%h%)-(4700*%a%)+500)/1000"
)
echo %result% is the caloric intake for %name%.
pause
exit /B
This is what I fixed:
set
and set /P
syntax is improved so that entire expression is in between quotation marks, hence you avoid trouble with special characters and you clearly can see if there are any trailing white-spaces;if
queries for gender entry are corrected so that they are now case-insensitive by adding switch /I
, and that they do not cause trouble in case the tested variables are empty by enclosing the comparison expressions in quotation marks;choice
command for such single-key entries, because it does not even accept any other characters;h
in inches is converted to 100ths of inches because of the said fixed-point arithmetics (but weight w
and age a
are still treated as integers); this is how it works:
set "h=%h%."
: append a dot to the entry in h
to ensure there is at least one;set "mil=1%h:*.=%"
: store everything after the first dot in h
into mil
(fractional part), and prepend 1
to the result to not lose any leading zeros (which are significant for the fractional part) as soon as this is converted to a number;set /A "h+=0, mil+=0"
: convert both h
and mil
to numeric values (integers); this converts everything up to the first non-numeric figure to a number, ignoring leading white-spaces but regarding signs +
/-
(although they are irrelevant as not needed here);set "mil=%mil%000"
: append 3 trailing zeros to the fractional part (the prepended 1
is still there, so there are always at least 4 digits);set "mil=%mil:~,4%"
: extract the first 4 characters (so the prepended 1
plus 3 more figures);set /A "mil+=5"
: add 5
, so if the last numeral is 5
or more, a carry appears, so rounding up of the next-to-last figure happens;if %mil:~,1% GTR 1 set /A "h+=1"
: increment integer part in h
in case the carry exceeds the fractional part;set "h=%h%%mil:~-3,-1%"
: concatenate the integer part of h
and the range from the second to the next-to-last figure of the fractional part in mil
and store it in h
;result
are multiplied by 1000
for the decimal dot to disappear, except the factor of h
which is multiplied by 10
only, because h
is already 100 times the original height value; afterwards the result is divided by 1000
; the addition of 500
means nothing but adding 0.5
when regarding the division by 1000
and is intended do round up fractional parts from .5
up to .9*
before they get lost due to the integer division;exit
command is replaced by exit /B
to terminate batch file but not the owning cmd
instance;Note that comments like //
and /*
, */
have no special meanings in batch-files. Comments or remarks are given by the rem
statement. I used the slash-style comments within rem
here only for some cool syntax highlighting here on this site...
As per a comment by the original poster, the result itself should be a fractional number, so I modified the script a bit (a description follows below):
@echo off
@title Calculate Caloric Needs
cls
set "name=them"
set /P "name=What is the name of the individual in question? "
set "male="
choice /C MF /M "Is %name% a M(ale) of F(emale)? "
if not ErrorLevel 2 set "male=Flag"
set "w=0"
set /P "w=What is the target weight of %name% IN POUNDS? "
set "h=0"
set /P "h=What is the height of %name% IN INCHES? "
set "h=%h%." & rem // append a decimal dot to entry in `h`
set "mil=1%h:*.=%" & rem // store everything behind first dot in `mil`, prepend `1`
set /A "h+=0, mil+=0" & rem // convert `h` to integer, dismiss fractional part
set "mil=%mil%000" & rem // pad trailing zeros to `mil`
set "mil=%mil:~,4%" & rem // extract first four numerals from `mil`
set /A "mil+=5" & rem // add `5` to `mil` for rounding
if %mil:~,1% GTR 1 set /A "h+=1" & rem // regard carry of previous addition in `h`
set "h=%h%%mil:~-3,-1%" & rem /* append second and third numeral of `mil` to `h`,
rem hence dismissing previously prepended `1`;
rem so `h` holds the height in 100ths of inches now */
set "a=0"
set /P "a=What is the age of %name% IN YEARS? "
rem // quotation marks avoid trouble with parenthesis or other characters;
rem /* all original constants are multiplied by `1000` to avoid fractional parts,
rem except the factor at `h` which is multiplied by `10` only due to above
rem implicit multiplication of `h` by 100, then `500` is added for rounding,
rem and finally, the result is divided by `1000` to remove the previous factors: */
if defined male (
set /A "result=66000+(6230*%w%)+(127*%h%)-(6800*%a%)+5"
) else (
set /A "result=655000+(4350*%w%)+(47*%h%)-(4700*%a%)+5"
)
echo %result:~,-3%.%result:~-3,-1% is the caloric intake for %name%.
pause
exit /B
This is what I did:
1000
has been omitted this time, and that 5
is added to the result for rounding instead of 500
; for displaying the result though, the very last figure is dismissed and a decimal dot .
is inserted before the remaining last two numerals, so there are two fractional digits for the result;choice
command as suggested in the original version of this answer (see above), so there is no more need to capture invalid results as choice
simply only accepts predefined keys or characters;