Division - Not Working

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
SIMMS7400
Posts: 546
Joined: 07 Jan 2016 07:47

Division - Not Working

#1 Post by SIMMS7400 » 02 Mar 2017 11:42

Hi Team -

I'm wondering why this wont work:

Code: Select all


SET "SEARCH_DIR=C:"
SET "FILE_SIZE=1073741824"
SET "OP=GEQ"

FOR /R "%SEARCH_DIR%" %%F IN (*) DO (

    IF EXIST "%%F" IF %%~zF %OP% %FILE_SIZE% (
   
      SET B=1073741824
      SET /A G=%%~zF/!B!
   
      ECHO %%F\File Size ^= %%~zF Bytes\!G! Gigabytes  >> %MAINPATH%%FILEPATH%Server_File_Check\%COMPUTERNAME%_%date:~-4,4%%date:~-10,2%%date:~-7,2%_%timestamp%.txt
   )
)


Since output is in bytes, I"m trying to convert to gigs.

Output is : Numbers are limited to 32 bits of precision.

Is this due to decimals? Any workarounds?

Thanks!

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: Division - Not Working

#2 Post by aGerman » 02 Mar 2017 12:03

The only numeric type that batch can calculate is a signed integer with a width of 32 bits. That means the greatest possible number is 2,147,483,647. If your files are larger you can't calculate with set /a directly.

SIMMS7400 wrote:Any workarounds?

Yes somewhere here at DosTips (most likely a couple of workarounds).

Steffen

SIMMS7400
Posts: 546
Joined: 07 Jan 2016 07:47

Re: Division - Not Working

#3 Post by SIMMS7400 » 02 Mar 2017 12:11

Thanks aGerman -

My largest file is : 2147475456

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: Division - Not Working

#4 Post by aGerman » 02 Mar 2017 12:16

Code: Select all

@echo off &setlocal
SET /A G=2147475456/1073741824
echo %G%
pause

Works well.

Steffen

ShadowThief
Expert
Posts: 1166
Joined: 06 Sep 2013 21:28
Location: Virginia, United States

Re: Division - Not Working

#5 Post by ShadowThief » 02 Mar 2017 12:22

Doing math with numbers other than 32-bit integers is really the only time I'd recommend using PowerShell (or VBScript/JScript/WScript/jesus-christ-Microsoft-just-pick-oneScript, since more people here seem to prefer that).

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Division - Not Working

#6 Post by Aacini » 02 Mar 2017 12:30

At this post a method to convert any number into its equivalent KB, MB, GB, etc is explained...

Antonio

SIMMS7400
Posts: 546
Joined: 07 Jan 2016 07:47

Re: Division - Not Working

#7 Post by SIMMS7400 » 02 Mar 2017 12:34

aGerman wrote:

Code: Select all

@echo off &setlocal
SET /A G=2147475456/1073741824
echo %G%
pause

Works well.

Steffen



Weird, that only resolves to "1" in my environment.

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: Division - Not Working

#8 Post by aGerman » 02 Mar 2017 12:42

SIMMS7400 wrote:Weird, that only resolves to "1" in my environment.

Not at all.
Remember:
aGerman wrote:The only numeric type that batch can calculate is a signed integer with a width of 32 bits.

Even 1.999999999999999999999 is 1.

Steffen

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: Division - Not Working

#9 Post by Squashman » 02 Mar 2017 12:57

This is a function that Judago posted many moons ago to do division on numbers larger than 32bit. We have a forum thread about this and other techniques to do this that are much shorter. He had several other native methods to do addition, subtraction, etc.......

This does do floating point math as well.

Code: Select all

:division

if "%~1"=="/?" (
    echo.&echo USAGE:&echo.
    echo "%~0" largenumber smallnumber [variablename] [places]
    echo "%~0" largenumber / smallnumber [variablename] [places]
    echo.&echo "largenumber" can floating point "smallnumber" can't.
    echo Only the first [places] decimal places are used for input or
    echo output. if [places] is omitted then the default of 8 is used.
    echo.&echo To specify [places] with out [variablename] pass an empty set.
    echo Below is an example:
    echo.&echo "%~0" 46546545464.123456789 / 1024 "" 9&echo.
    echo.&echo "smallnumber" must be below 2097153, "largenumber" can have
    echo hundreds of places.&echo.&echo -Judago 2009/2010
    exit /b 0
)
SETLOCAL ENABLEDELAYEDEXPANSION
set error=Invalid Input
if "%~1"=="" goto error
if "%~2"=="/" shift /2
if "%~2"=="" goto error
for /f "delims=1234567890." %%a in ("%~1%~2") do goto error
set divisor=%~2
set error=Divisor must be whole
if not "!divisor!"=="!divisor:.=!" goto error
if !divisor! gtr 2097152 (
    set error=Divisor too large, limited to: 2097152
    goto error
)
set dplace=%~4
if not defined dplace set dplace=8
for /f "delims=1234567890." %%a in ("%~4") do set dplace=8
set input=0%~1
for /l %%a in (1 1 %dplace%) do set input=!input!0
set error=Divide by zero
if "!divisor:0=!"=="" goto error
set chunk=
set total=

set /a fpos=dplace + 1
:isfloat
if not "!input:.=!"=="!input!" (
    if not "!input:~-%fpos%,1!"=="." (
        set input=!input:~0,-1!
        goto isfloat
    ) else (
        set input=!input:.=!
    )
)

:split
if not "%input:~3%"=="" (
    set /a chunk+=1
    set input!chunk!=%input:~-3%
    set input=%input:~0,-3%
    if defined input goto split
) else (
    set /a chunk+=1
    set input!chunk!=%input%
)

:loop
if defined input%chunk% (
    if "!input%chunk%:~0,1!"=="0" (
        set input%chunk%=!input%chunk%:~1!
        goto loop
    )
) else (
    set input%chunk%=0
    goto pad
)
set chunkresult=0

:divide
If !input%chunk%! geq !divisor! (
    If !input%chunk%! geq !divisor!000 (
        set /a input%chunk%-=!divisor!000
        set /a chunkresult+=1000
        goto divide
    ) else (
        If !input%chunk%! geq !divisor!00 (
            set /a input%chunk%-=!divisor!00
            set /a chunkresult+=100
            goto divide
        ) else (
            If !input%chunk%! geq !divisor!0 (
                set /a input%chunk%-=!divisor!0
                set /a chunkresult+=10
                goto divide
            ) else (
                set /a input%chunk%-=!divisor!
                set /a chunkresult+=1
                goto divide
            )
        )
    )
)
:pad
if "!chunkresult:~2,1!"=="" set chunkresult=0!chunkresult!
if "!chunkresult:~2,1!"=="" set chunkresult=0!chunkresult!
set total=%total%%chunkresult%
set chunkresult=0
if %chunk% gtr 0 (
    set /a chunk-=1
    if !input%chunk%! gtr 0 (
        set carry=!input%chunk%!
        for %%a in (!chunk!) do set input!chunk!=!carry!!input%%a!
    )
)
if %chunk% gtr 0 goto loop
if not defined total set total=0
if %dplace% gtr 0 set total=!total:~0^,-%dplace%!.!total:~-%dplace%!

:finish
if "%total:~0,1%"=="0" if not "%total:~1%"=="" set total=%total:~1%&&goto finish
if "%total:~0,1%"=="." set total=0%total%
set "mod=!input%chunk%!"
IF NOT DEFINED mod SET mod=0
if not "%~3"=="" (
    endlocal
    set %~3=%total%
    set "%~5=%mod%"
) else (
    echo %total% - Leftover:!input%chunk%! ^(this isn't always the mod result^)
    endlocal
)
exit /b 0

:Error
1>&2 echo %error% - See "%~0 /?"
endlocal
exit /b 1

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: Division - Not Working

#10 Post by dbenham » 02 Mar 2017 13:22

Working with large file sizes is often awkward with batch because of the 32 bit signed integer limitation. Even the IF GEQ comparison is limited to 32 bit signed integers. Values greater than 2147483647 (0x7FFFFFFF) are all treated as equal to 2147483647. So your script would fail if your threshold were something like 3 GB instead of 1 GB. See http://stackoverflow.com/q/9116365/1012053 for more information.

Then you have the problem you have already discovered with trying to convert numbers into GB or MB using batch. There are simple ways to approximate the computation, as well as multiple simple (but relatively slow) ways to use hybrid scripting to do decimal math with large numbers. Then you have the complex batch scripting that Squashman points out to do precise decimal arithmetic.

But I recently learned that ROBOCOPY can solve this problem very easily. The /MIN:size option handles large numbers, and the displayed size is automatically converted into MB or GB if the file exceeds 1 MB. You can add the /BYTES option to display all sizes as bytes, without any conversion. But it cannot list both bytes and gigabytes as you have in your script.

Code: Select all

robocopy c:\ c:\ /l /s /is /njh /njs /ndl /nc /min:1073741824 /xj

The output is not exactly formatted as you want, but it probably will work for you. For example, here is a partial listing of the output from my C: drive:

Code: Select all

                           2.9 g        c:\hiberfil.sys
                           5.7 g        c:\pagefile.sys
                           3.4 g        c:\test\huge.txt
                           1.7 g        c:\test\testbig.txt

You could use FOR /F to easily parse the output if you want to take control of the formatting.

Code: Select all

@echo off
for /f "tokens=1,2*" %%A in (
  'robocopy c:\ c:\ /l /s /is /njh /njs /ndl /nc /min:1073741824 /xj'
) do echo %%A GB  -  %%C
--OUTPUT--

Code: Select all

2.9 GB - c:\hiberfil.sys
5.7 GB - c:\pagefile.sys
3.4 GB - c:\test\huge.txt
1.7 GB - c:\test\testbig.txt

EDIT - Added /XJ option to prevent possibility of endless cycling due to junction points


Dave Benham

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Division - Not Working

#11 Post by Aacini » 02 Mar 2017 13:33

Code: Select all

@echo off
setlocal

set GB=1073741824

:nextSize
echo/
set /P "file=File size: "
if errorlevel 1 goto :EOF

set /A Int=file/GB, Frac=file-Int*GB
set "Frac=%Frac:~0,-5%"
if defined Frac set /A Frac=%Frac%00000 / 10738
set "Frac=00000%Frac%"
echo %Int%.%Frac:~-5% GB
goto nextSize

Output example:

Code: Select all

File size: 2147475456
1.99990 GB

File size: 2000000000
1.86254 GB

File size: 1073741824
1.00000 GB

File size: 1073741823
0.99990 GB

File size: 1000000000
0.93127 GB

File size: 500000000
0.46563 GB

File size: 50000000
0.04656 GB

File size: 5000000
0.00465 GB

File size: 500000
0.00046 GB

File size: 50000
0.00000 GB

File size:

Antonio

SIMMS7400
Posts: 546
Joined: 07 Jan 2016 07:47

Re: Division - Not Working

#12 Post by SIMMS7400 » 02 Mar 2017 14:36

Thanks, all! THis is great!

Having trouble setting Antonio's solution up in the for loop.

Here is what I have:

Code: Select all

FOR /R "%SEARCH_DIR%" %%F IN (*) DO (

   IF EXIST "%%F" IF %%~zF GEQ %FILE_SIZE% (

set "file=%%~zF"
set /A Int=file/GB, Frac=file-Int*GB
set "Frac=%Frac:~0,-5%"
if defined Frac set /A Frac=%Frac%00000 / 10738
set "Frac=00000%Frac%"
echo !Int!.!Frac:~-5! GB

   ECHO %%F\File Size ^= %%~zF B ^| !Int!.!Frac:~-5! GB  >> %MAINPATH%%FILEPATH%Server_File_Check\%COMPUTERNAME%_%date:~-4,4%%date:~-10,2%%date:~-7,2%_%timestamp%.txt
   )
)

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: Division - Not Working

#13 Post by Squashman » 02 Mar 2017 15:30

SIMMS7400 wrote:Thanks, all! THis is great!

Having trouble setting Antonio's solution up in the for loop.

Here is what I have:

Code: Select all

FOR /R "%SEARCH_DIR%" %%F IN (*) DO (

   IF EXIST "%%F" IF %%~zF GEQ %FILE_SIZE% (

set "file=%%~zF"
set /A Int=file/GB, Frac=file-Int*GB
set "Frac=%Frac:~0,-5%"
if defined Frac set /A Frac=%Frac%00000 / 10738
set "Frac=00000%Frac%"
echo !Int!.!Frac:~-5! GB

   ECHO %%F\File Size ^= %%~zF B ^| !Int!.!Frac:~-5! GB  >> %MAINPATH%%FILEPATH%Server_File_Check\%COMPUTERNAME%_%date:~-4,4%%date:~-10,2%%date:~-7,2%_%timestamp%.txt
   )
)


I see two problems
1) You do not set a value to a variable named GB
2) You are now inside a code block so that changes how the values are expanded.

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: Division - Not Working

#14 Post by dbenham » 02 Mar 2017 16:06

I just realized it is trivial to extend the ROBOCOPY solution to provide your exact output. The FOR loop can be used to get the size in bytes for each file.

Code: Select all

@echo off
for /f "tokens=1,2*" %%A in (
  'robocopy c:\ c:\ /l /s /is /njh /njs /ndl /nc /min:1073741824 /xj
) do echo %%C\File Size ^= %%~zC Bytes\%%A Gigabytes

--Example OUTPUT--

Code: Select all

c:\hiberfil.sys\File Size =  Bytes\2.9 Gigabytes
c:\pagefile.sys\File Size =  Bytes\5.7 Gigabytes
c:\test\huge.txt\File Size = 3724541953 Bytes\3.4 Gigabytes
c:\test\testbig.txt\File Size = 1862270977 Bytes\1.7 Gigabytes

The first two files show blank Bytes because they are flagged as Hidden and System, so the FOR variable cannot access them to get the size. Your FOR /R code does not show them at all.

It is easy to eliminate the Hidden and System files from the ROBOCOPY output by adding the /XA:HS option

Code: Select all

@echo off
for /f "tokens=1,2*" %%A in (
  'robocopy c:\ c:\ /l /s /is /njh /njs /ndl /nc /min:1073741824 /xj /xa:hs'
) do echo %%C\File Size ^= %%~zC Bytes\%%A Gigabytes


The code now precisely gives your desired output, and ROBOCOPY is significantly faster at filtering out the large files than any FOR loop you could write. The ROBOCOPY solution FOR loop need only iterate the large files, but a simple FOR /R must iterate all files within the folder hierarchy. So the ROBOCOPY should be much faster. I don't see any reason not to use ROBOCOPY.

EDIT - Added /XJ option to prevent possibility of endless cycling due to junction points


Dave Benham

SIMMS7400
Posts: 546
Joined: 07 Jan 2016 07:47

Re: Division - Not Working

#15 Post by SIMMS7400 » 02 Mar 2017 16:09

Ah yes, Squash. Sorry about that. I t was set above, but I decided to utlize one I already had.

Code: Select all

FOR /R "%SEARCH_DIR%" %%F IN (*) DO (

   IF EXIST "%%F" IF %%~zF GEQ %FILE_SIZE% (

   set "file=%%~zF"
   set /A Int=file/FILE_SIZE, Frac=file-Int*FILE_SIZE
   set "Frac=!Frac:~0,-5!"
   if defined Frac set /A Frac=!Frac!00000 / 10738
   set "Frac=00000!Frac!"

   ECHO %%F\File Size ^= %%~zF B ^| !Int!.!Frac:~-5! GB  >> %MAINPATH%%FILEPATH%Server_File_Check\%COMPUTERNAME%_%date:~-4,4%%date:~-10,2%%date:~-7,2%_%timestamp%.txt
   )
)



Got it, pesky expansion! Thanks, all this great!!!! Always a pleasure learning more.

Post Reply