Update:

バッチ処理サンプル(その2)「多桁計算」

Windowsの標準コマンドで作成したプログラム(?)達です。

Windows 2000、XPで使えるようになった拡張機能を利用しています。
たいした処理はしていませんが、何かの参考や役に立てば幸いです。
※Windows 9x/meでは利用できません。ご了承ください。

数値計算

多桁(10進数64桁まで)の整数計算です。
コマンドプロンプトでは、SETコマンドで数値計算ができるようになっています。
2進数32ビットの整数計算しかサポートしていないので、少し工夫し10進数64桁まで計算させます。(掛け算結果で約128桁まで求められます。)
この計算では、10進数4桁を1ブロックとしてブロック単位で計算しています。(理論的には10000進数に相当します。)
計算桁数は、理論的にはPCのメモリ容量で制限されますが、現実的には計算時間や結果の画面表示方法などの問題が発生するため、10進数4桁/ブロックで64桁(16ブロック分)までに制限しています。
また、整数計算の方法としては、10進数2進数変換を行って、2進数で計算する方法もありますがここでは取り上げません。(10進数⇔2進数の変換に時間がかかるためです。)

ページトップへ戻る

足し算(加算)

このサブルーチンでは、負数の計算もサポートしています。
負数は、100000の補数形式で計算しますので結果を返すときに符号付10進数に変換しています。

@title 多桁計算(64桁まで)
@echo off
REM ----+----1----+----2----+----3----+----4----+----5----+----6----
set sp=0000000000000000000000000000000000000000000000000000000000000000
set sp=0000000000000000000000000000000000000000000000000000000000000000%sp%
set /p na=数字1:
set /p nb=数字2:
call :set_num a na
call :set_num b nb
echo.

REM 加算
set s=
set /a cb=0
call :add
echo 加算の結果:%add%

pause
echo on
@goto :EOF
REM -------------------------ここからサブルーチン
REM 数値を4桁づつのブロックに分割
:set_num
  set arg=%2
  call set arg=%%%arg%%%
  set sign%1=
  if "%arg:~0,1%" == "-" set arg=%arg:~1%&set sign%1=-
  set wk_num=%sp%%arg%
  set wk_num=%wk_num:~-128%
  for /l %%I in (0, 1, 31) do call :num_div %1 %%I
goto :EOF
:num_div
  set /a %1[%2]=1%wk_num:~-4% - 10000
  set wk_num=%wk_num:~0,-4%
goto :EOF


REM 足し算
:add
  for /l %%I in (0,1,31) do call :add_main %%I
  call :complement9
goto :EOF
:add_main
  call set /a wk_num=(%signa%%%a[%1]%%) + (%signb%%%b[%1]%%) + (%cb%) + 100000
  if %wk_num:~0,1% == 9 (
    set /a cb=-1
    set /a wk_num=%wk_num% - 90000 + 100000
  ) else (
    if %wk_num:~0,1% == 8 (
      set /a cb=-2
      set /a wk_num=%wk_num% - 80000 + 100000
    ) else (
      set /a cb=%wk_num:~1,1%
      set /a wk_num=%wk_num% - %wk_num:~0,2% * 10000 + 100000
    )
  )
  set wk_num=%wk_num:~2,4%
  set s=%wk_num%%s%
goto :EOF

REM 表示形式の変換
:complement9
  set sign=
  if %s:~0,1% == 9 goto :nine
  if %s:~0,1% == 0 goto :zero
  set add=%s%
goto :EOF
:nine
  set sign=-
  set sol=
  for /l %%I in (0,1,31) do call :set_sol %%I
  set s=%sol%
  :zero
  set s=%s%.
  :loop_zero
  if %s:~0,1% == 0 set s=%s:~1%& goto loop_zero
  set s=%s:~0,-1%
  if "%s%" == "" set s=0
  set add=%sign%%s%
goto :EOF

REM 補数を通常数に変換
:set_sol
  set /a si=%1 * 4
  call set /a wk_num = 9999 - %%s:~%si%,4%%
  if %1 == 31 set /a wk_num = %wk_num% + 1
  set wk_num=0000%wk_num%
  set sol=%sol%%wk_num:~-4%
goto :EOF

ページトップへ戻る

引き算(減算)

引き算(減算)も負数を扱うと足し算(加算)とほぼ同じプログラムになります。(足し算の記述と見比べてください。)

@title 多桁計算(64桁まで)
@echo off
REM ----+----1----+----2----+----3----+----4----+----5----+----6----
set sp=0000000000000000000000000000000000000000000000000000000000000000
set sp=0000000000000000000000000000000000000000000000000000000000000000%sp%
set /p na=数字1:
set /p nb=数字2:
call :set_num a na
call :set_num b nb
echo.

REM 減算
set s=
set /a cb=0
call :sub
echo 減算の結果:%sub%

pause
echo on
@goto :EOF

REM -------------------------ここからサブルーチン
REM 数値を4桁づつのブロックに分割
:set_num
  set arg=%2
  call set arg=%%%arg%%%
  set sign%1=
  if "%arg:~0,1%" == "-" set arg=%arg:~1%&set sign%1=-
  set wk_num=%sp%%arg%
  set wk_num=%wk_num:~-128%
  for /l %%I in (0, 1, 31) do call :num_div %1 %%I
goto :EOF
:num_div
  set /a %1[%2]=1%wk_num:~-4% - 10000
  set wk_num=%wk_num:~0,-4%
goto :EOF

REM 引き算
:sub
  for /l %%I in (0,1,31) do call :subtract %%I
  call :complement9
goto :EOF

:subtract
  call set /a wk_num=(%signa%%%a[%1]%%) - (%signb%%%b[%1]%%) + (%cb%) + 100000
  rem call echo set /a %wk_num%=(%signa%%%a[%1]%%) - (%signb%%%b[%1]%%) + (%cb%) + 100000
  if %wk_num:~0,1% == 9 (
    set /a cb=-1
    set /a wk_num=%wk_num% - 90000 + 100000
  ) else (
    if %wk_num:~0,1% == 8 (
      set /a cb=-2
      set /a wk_num=%wk_num% - 80000 + 100000
    ) else (
      set /a cb=%wk_num:~1,1%
      set /a wk_num=%wk_num% - %wk_num:~0,2%0000 + 100000
    )
  )
  rem echo wk_num=%wk_num:~2,4%
  set wk_num=%wk_num:~2,4%
  set s=%wk_num%%s%
  rem echo %s%
goto :EOF

REM 表示形式の変換
:complement9
  set sign=
  if %s:~0,1% == 9 goto :nine
  if %s:~0,1% == 0 goto :zero
  set sub=%sol%
goto :EOF
:nine
  set sign=-
  set sol=
  for /l %%I in (0,1,31) do call :set_sol %%I
  set s=%sol%
  :zero
  set s=%s%.
  :loop_zero
  if %s:~0,1% == 0 set s=%s:~1%& goto loop_zero
  set s=%s:~0,-1%
  if "%s%" == "" set s=0
  set sub=%sign%%s%
goto :EOF

REM 補数を通常数に変換
:set_sol
  set /a si=%1 * 4
  call set /a wk_num = 9999 - %%s:~%si%,4%%
  if %1 == 31 set /a wk_num = %wk_num% + 1
  set wk_num=0000%wk_num%
  set sol=%sol%%wk_num:~-4%
goto :EOF

ページトップへ戻る

掛け算(乗算)

筆算と同じ計算方法を採っているので比較的理解しやすいと思います。
計算を全ブロックで行っているため少し遅いです。
計算中のイライラを避けるために、計算中は「.」を表示するようにしてあります。

@title 多桁計算(64桁まで)
@echo off
REM ----+----1----+----2----+----3----+----4----+----5----+----6----
set sp=0000000000000000000000000000000000000000000000000000000000000000
set sp=0000000000000000000000000000000000000000000000000000000000000000%sp%
set /p na=数字1:
set /p nb=数字2:

call :set_num a na
call :set_num b nb
echo.

REM 乗算
set s=
set /a cb=0
call :mult
echo 乗算の結果:%mult%
echo on
@exit /b

REM -------------------------ここからサブルーチン
REM 数値の分割
:set_num
  set arg=%2
  call set arg=%%%arg%%%
  set sign%1=
  if "%arg:~0,1%" == "-" set arg=%arg:~1%&set sign%1=-
  set wk_num=%sp%%arg%
  set wk_num=%wk_num:~-128%
  for /l %%I in (0, 1, 31) do call :num_div %1 %%I
goto :EOF
:num_div
  set /a %1[%2]=1%wk_num:~-4% - 10000
  set wk_num=%wk_num:~0,-4%
goto :EOF

REM 掛け算
:mult
  for /l %%I in (0, 1, 31) do set temp[%%I]=0
  set /p dmy=掛け算の計算中.<nul
  for /l %%I in (0, 1, 31) do call :multiply %%I
  set s=
  for /l %%I in (0, 1, 31) do call :mult_s %%I
  set s=%s%.
  :mult_loop_zero
  if %s:~0,1% == 0 set s=%s:~1%& goto mult_loop_zero
  set s=%s:~0,-1%
  if "%s%" == "" set s=0
  set /a sign = %signa%1 * %signb%1
  if %sign% LSS 0 set s=-%s%
  echo 計算終了
  set mult=%s%
goto :EOF

REM 掛け算の結果を文字列に変換します。
:mult_s
  call set /a temp[%1]=10000+%%temp[%1]%%
  call set s=%%temp[%1]:~1%%%s%
goto :EOF

REM 掛け算を行います。
:multiply
  set /a i=0
  set /a cb=0
  :multiply_loop_01
    set /a pos=%i%+%1
    call set /a wk_num=%%a[%i%]%% * %%b[%1]%% + %cb% + %%temp[%pos%]%%
    set /a cb=%wk_num%/10000
    set /a temp[%pos%]=%wk_num%-%cb%*10000
    set /a i+=1
  if %i% LSS 16 goto multiply_loop_01
  set /p dmy=. goto :EOF

ページトップへ戻る

割り算(除算)

四則計算の中で最も難易度の高い計算です。
「被除数(a)÷除数(b)=商(q)・・・余り(r)の計算結果を求めます。
アルゴリズムは最も簡単な部類である、除数を2倍する操作を繰り返して被除数を超えない最大の倍数を引いていきます。
「除数を2倍する操作」と「被除数と除数の倍数の比較」、「被除数から除数の倍数を引く操作」が繰り返されるため非常に計算時間がかかります。
Celeron2.4G、メモリ512MB(うちフリー126MB)、WindowsXPの環境で 12345678987654321 ÷ 111111111 = 111111111 ・・・ 0 の計算に344秒かかりました。
桁数、ブロック数の調整を行えば、もう少し処理時間も改善できると思います。

@title 多桁計算(64桁まで)
@echo off
REM ----+----1----+----2----+----3----+----4----+----5----+----6----
set sp=0000000000000000000000000000000000000000000000000000000000000000
set sp=0000000000000000000000000000000000000000000000000000000000000000%sp%
set /p na=数字1:
set /p nb=数字2:
rem pause
call :set_num a na
call :set_num b nb
echo.
rem set /a tm0=%time:~0,2%*3600+%time:~3,2%*60+%time:~6,2%
REM 除算
call :divid
rem set /a tm1=%time:~0,2%*3600+%time:~3,2%*60+%time:~6,2%
rem set /a tm=tm1-tm0
echo 除算の結果:%divid% 余り:%remainder%
rem echo 計算時間:%tm% [秒]
pause
echo on
@goto :EOF

REM -------------------------ここからサブルーチン
REM 数値の分割
:set_num
  set arg=%2
  call set arg=%%%arg%%%
  set sign%1=
  if "%arg:~0,1%" == "-" set arg=%arg:~1%&set sign%1=-
  set wk_num=%sp%%arg%
  set wk_num=%wk_num:~-128%
  for /l %%I in (0, 1, 31) do call :num_div %1 %%I
goto :EOF
:num_div
  set /a %1[%2]=1%wk_num:~-4% - 10000
  set wk_num=%wk_num:~0,-4%
goto :EOF

REM 割り算
:divid
  REM a[]:数値a, b[]:数値b
  REM k[]:数値aの作業変数, w[]:数値bの作業変数(これを2倍づつ増加させてk[]と比較します。)
  REM c[]:数値aを超えない数値bの倍数(w[]の1/2)を格納しておきます。
  REM q[]:数値bの倍率を求めます。
  REM qq[]:数値aを超えない数値bの倍率を記録しておきます。(q[]の1/2)
  REM temp[]:数値qq[]の合計を記録します。(これが商の値となります。)
  REM q:w[]を2倍した回数を記録します。
  REM kk:k[i]の数値を入れてwwと比較します。, ww:w[i]の数値を入れてkkと比較します。
  REM dmy:ダミー変数で、一時的な作業に使用します。
  REM cb:各種計算の桁上がりや桁借りを入れておきます。
  REM wk_num:計算の途中経過を一時的に格納する変数です。
  REM signa:数値aの符号, signb:数値bの符号, sign:計算結果の符号
  REM i:繰り返し回数をカウントするための変数です。
  REM s:計算結果を表示します。
  REM r:余りを表示します。
  
  :divid_label1
  set /p dmy=割り算の計算中.<nul
  set s=
  for /l %%I in (0,1,31) do call set k[%%I]=%%a[%%I]%%
  for /l %%I in (0,1,31) do call set w[%%I]=%%b[%%I]%%
  for /l %%I in (0,1,31) do set q[%%I]=0&set qq[%%I]=0
  for /l %%I in (0,1,31) do set temp[%%I]=0
  set q[0]=1&set qq[0]=1
  set q=0
  set k[-1]=1
  set w[-1]=0
  set i=32
  :divid_label2
    set /a i-=1
    call set kk=%%k[%i%]%%
    call set /a ww=%%w[%i%]%%
    if %ww% EQU %kk% goto divid_label2
    set /p dmy=.<nul
    if %ww% LSS %kk% call :doubler&goto divid_label2
    if %q% LSS 1 goto divid_label3
      set cb=0
      for /l %%I in (0,1,31) do call :divid_temp %%I
      set q[0]=1
      call :subtracter
      for /l %%I in (0,1,31) do call set w[%%I]=%%b[%%I]%%
      set i=32
      set q=0
    goto divid_label2
  :divid_label3
  call :remainder
  for /l %%I in (0, 1, 31) do call :divid_s %%I
  set s=%s%.
  :divid_loop_zero2
  if %s:~0,1% == 0 set s=%s:~1%& goto divid_loop_zero2
  set s=%s:~0,-1%
  if "%s%" == "" set s=0
  set /a sign = %signa%1 * %signb%1
  if %sign% LSS 0 set s=-%s%
  echo 計算終了
  set divid=%s%
  set remainder=%r%
goto :EOF

REM 倍率の集計を行います。
:divid_temp
  call set /a wk_num = %%temp[%1]%% + %%qq[%1]%% + %cb%
  set /a cb=%wk_num%/10000
  set /a temp[%1]=%wk_num%-%cb%*10000
  set q[%1]=0
goto :EOF

REM 割り算結果(商)の表示形式を調整します。
:divid_s
  call set /a wk_num = %%temp[%1]%% + 10000
  set s=%wk_num:~1,4%%s%
goto :EOF

REM 除数を2倍します。
:doubler
  set cb=0
  set i=0
  :doubler_loop_01
    call set c[%i%]=%%w[%i%]%%
    call set /a w[%i%]=%%c[%i%]%% + %%c[%i%]%% + %cb%
    call set /a cb=%%w[%i%]%%/10000
    call set /a w[%i%]=%%w[%i%]%% - %cb% * 10000
    set /a i+=1
  if %i% LSS 32 goto doubler_loop_01
  set cb=0
  set /a q+=1
  for /l %%I in (0,1,31) do call :calc_q %%I
  set i=32
goto :EOF

REM 倍率を計算します。
:calc_q
  call set qq[%1]=%%q[%1]%%
  call set /a wk_num=2*%%q[%1]%%+%cb%
  set /a cb=%wk_num%/10000
  set /a q[%1]=%wk_num%-%cb%*10000
goto :EOF

REM 被除数から除数の倍数を引きます。
:subtracter
  set /a i=0
  set cb=0
  :subtracter_loop
    call set /a ww=%%k[%i%]%%-%%c[%i%]%%-%cb%
    if %ww% LSS 0 (
      set /a ww=10000+%ww%
      set /a cb=1
    ) else (
      set cb=0
    )
    set k[%i%]=%ww%
    set /a i+=1
  if %i% LSS 32 goto subtracter_loop
goto :EOF

REM 余りを求めます。
:remainder
  set r=
  for /l %%I in (0, 1, 31) do call set /a k[%%I]=%%k[%%I]%%+10000
  set i=0
  :remainder_loop
    call set r=%%k[%i%]:~1%%%r%
    set /a i+=1
  if %i% LSS 32 goto remainder_loop
  set r=%r%.
  :remainder_loop_zero
  if %r:~0,1% == 0 set r=%r:~1%& goto remainder_loop_zero
  set r=%r:~0,-1%
  if "%r%" == "" set r=0
goto :EOF

ページトップへ戻る

【ページ内見出し】
【サイト内リンク】

【広告エリア】



元のページに戻る  ページを閉じる

連絡先:お問い合わせフォーム