0

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

phuclv
  • 37,963
  • 15
  • 156
  • 475
eapo
  • 1,053
  • 1
  • 19
  • 40
  • 6
    It has nothing to do with limits in time measurement. It has to do with the precision of integers, which are signed 32-bit in command or batch files, IIRC. – Ken White Jan 13 '19 at 04:06
  • don't use spaces around `=` in batch like `SET /a s = 60 * m`. [Don't use ping to sleep](https://stackoverflow.com/q/1672338/995714). And 2147483647 is simply 2³¹-1 which is the largest 32-bit integer (which cmd uses). Besides `set /a` doesn't support floating-point operations, thus `set /a 1037950429,99999999*60` is exactly the same as `set /a 99999999*60`. If you want to use 64-bit int or double then you must use other scripting languages like powershell or VBS – phuclv Jan 13 '19 at 04:15
  • Also, arbitrarily deciding that a certain number of minutes starts at any base point in time is a mistake. You've correlated that (incorrectly) to June 10, 1973 at 9:00, but that isn't accurate. It's just a certain number of minutes - there's no beginning reference point for those minutes other than the one you've decided to assign. IOW, if I start a timer now and let it run for 10 minutes, the beginning reference point of that timer is not in 1970, but the time when I started the timer. Your `1973` means 1973 years period, not 1973 years from a specific starting point. 2019+1973 is valid. – Ken White Jan 13 '19 at 04:17
  • 1
    To get the maximum positive number in 32-bits precision, enter `set /A 0x7FFFFFFF` at the command prompt. For the minimum negative number, enter `set /A 0x80000000` – Aacini Jan 13 '19 at 17:05
  • Thank you for your amazing comments, now i have all of my answers! Paying with the date was just fun ;) – eapo Jan 17 '19 at 22:50

1 Answers1

3

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

phuclv
  • 37,963
  • 15
  • 156
  • 475