'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:
- the maximum seconds that I can use, is exactly the eighth Mersenne prime:
2147483647
(2147483647 seconds to years at DuckDuckGo), but - if I use the multiplier
60
to start the countdown with minutes, the maximum accepted integer is1037950429.99999999
.- 1037950429.99999999 minutes to years equals
1973.43904479
which refers to the day of June 10, 1973 9:00 AM, but from my research nothing special happened in that day.
- 1037950429.99999999 minutes to years equals
Doing my research to understand why(?), led me to ask this question.
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.
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 expressionsset /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 |