'Time maximums in Windows shell and Why?

I made a simple countdown in Windows Batch Scripting:

@ECHO OFF
MODE con: cols=13 lines=2
COLOR 4f
TITLE Countdown Timer
SET /p m=? Minutes:
SET /a s = 60 * m
FOR /l %%a in (%s%,-1,1) do (
  ECHO %%a/%s%
  PING -n 2 127.0.0.1 >NUL 2>&1
  CLS
)
IF ERRORLEVEL 1 EXIT
CSCRIPT alert.vbs
EXIT

and I noticed the following things:

  1. the maximum seconds that I can use, is exactly the eighth Mersenne prime: 2147483647 (2147483647 seconds to years at DuckDuckGo), but
  2. if I use the multiplier 60 to start the countdown with minutes, the maximum accepted integer is 1037950429.99999999.

Doing my research to understand why(?), led me to ask this question.

windows command calculations



Solution 1:[1]

Firstly, this is an XY problem because Windows already has a command for counting down: timeout. There's no reason to re-implement that unless you want more control over it


Regarding the observed behavior, cmd.exe (as well as its set /a internal command) uses only 32-bit integers, therefore the maximum value it can represent is 2147483647 = 232 - 1

The numbers must all be within the range of 32 bit signed integer numbers (-2,147,483,648 through 2,147,483,647) to handle larger numbers use PowerShell or VBScript.

https://ss64.com/nt/set.html

It's an extremely common constant in computers, since we almost all use 32-bit int. The correlation to Mersenne prime is just purely accidental due to the choice of bit width

And from the above link you can see comma is a separator operator just like in C-like languages, not a radix point. In fact I've never seen a programming language that uses comma as radix point like in written languages.

, Commas separate expressions set /a "_num=2,_result=_num*5"

If you run set /? you'll see the comma in the precedence table

The /A switch specifies that the string to the right of the equal sign
is a numerical expression that is evaluated.  The expression evaluator
is pretty simple and supports the following operations, in decreasing
order of precedence:

()                  - grouping
! ~ -               - unary operators
* / %               - arithmetic operators
+ -                 - arithmetic operators
<< >>               - logical shift
&                   - bitwise and
^                   - bitwise exclusive or
|                   - bitwise or
= *= /= %= += -=    - assignment
  &= ^= |= <<= >>=
,                   - expression separator

That means 1037950429,999999999*60 is simply 2 expressions, one calculates 1037950429 and discard, and the other calculates 999999999*60, which overflows 32-bit int and returns a negative value as you see

C:\Users\>set /a 1037950429,999999999*60
-129542204
C:\Users\>set /a 999999999*60
-129542204
C:\Users\>set /a 1037950429,99999999*60
1705032644
C:\Users\>set /a 99999999*60
1705032644
C:\Users\>set /a 1037950429.999999999*60
Missing operator.

Even if you want to do operations with floating-point values such as 1037950429.999999999 then you're out of luck since that exceeds the precision of the biggest binary floating-point type in VBS and PowerShell which is IEEE-754 double precision and can only be accurate to ~15 decimal digits. PowerShell does have a 128-bit decimal floating-point type (inherited from .NET's decimal) that helps a bit in that case though:

PS C:\Users> 1037950429.999999999d * 60
62277025799.999999940

Also note that set a = b in batch means assigning a string b with a space before into a variable named %a %. Spaces are significant in set command, thus don't use spaces around the = operator. See

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1