Войти

Показать полную графическую версию : Расширение 32-битной арифметики


NiOl
01-11-2010, 19:24
Казалось, что данный вопрос уже поднимался в ветке, но ничего не нашлось...

Предлагаю поделиться наработками по увеличению разрядности арифметики - 32 бита обычно хватает, но только до поры, пока не начинается работа с кучей файлов и их размеры не вылазят за 4ГБ...

однажды я наскоро "заткнул" брешь введя отдельно накопитель в гигабайтах, но выглядело это неказисто и работало только в плане сравнения чисел, без вывода на экран...
после этого делал пару "подходов" - хотелось "красиво" реализовать 4 базовые функции и функции по представлению данных (перевод в текст и обратно), но никчему так и не пришел.
:sorry:

kiripanda
01-11-2010, 21:36
Есть консольные калькуляторы

amel27
02-11-2010, 12:50
NiOl, имхо "универсальные" варианты противоречат духу скриптов... ;)
утилит много (тот же AWK), да и WSH всегда под рукой, можно даже совместить в одном файле

на пробу, вариант суммы/разности для 16-разрядных чисел ~ 32 петабайта:
@echo off

call :SUM -99000900007 -99000900007
pause>nul& exit

:SUM %int% %int% [%VarSum%]
SETLOCAL EnableDelayedExpansion& set "$af=%~1"& set "$bf=%~2"
for /f "tokens=* delims=+-0" %%i in ("%$af%") do set "$am=%%i"
for /f "tokens=* delims=+-0" %%i in ("%$bf%") do set "$bm=%%i"
for /f "tokens=* delims=0" %%i in ("%$am:~-8%") do set /a "$ah=%$af:~,-8%+0,$al=%%i+0"& if !$ah! lss 0 set "$al=-!$al!"
for /f "tokens=* delims=0" %%i in ("%$bm:~-8%") do set /a "$bh=%$bf:~,-8%+0,$bl=%%i+0"& if !$bh! lss 0 set "$bl=-!$bl!"

set /a "$sl=($al)+($bl)"& set "$ml=!$sl:-=!"
set /a "$sh=($ah)+($bh)+(%$sl:~,-8%+0)"

if %$sh% gtr 0 if %$sl% lss 0 set /a "$sh-=1,$sl=100000000-%$ml:~-8%"
if %$sh% lss 0 if %$sl% gtr 0 set /a "$sh+=1,$sl=%$ml:~-8%-100000000"
set $ml=%$sl:-=%& set $ml=0000000!$ml:~-8!
if %$sh% equ 0 (set "$sf=!$sl!") else set "$sf=%$sh%!$ml:~-8!"
for /f "delims=" %%i in ("%$sf%") do (
ENDLOCAL& if "%~3"=="" (echo %%i) else set "%~3=%%i")
GoTo :EOF

P.S. алгоритм прост:

- делим число на 2 части по восемь 10-тичных разрядов;
- складываем отдельно младшую/старшую части;
- обрабатываем перенос из младшей в старшую;
- соединяем в одну строку.

amel27
03-11-2010, 12:48
обобщение на случай чисел любой длины (до 1024 десятичных знаков):
:SUM %int% %int% [%VarSum%]
::----------------------------
SETLOCAL EnableDelayedExpansion& set "$a=%~1"& set "$b=%~2"
if "%$a:~,1%"=="-" (set "$az=-"&set "$a=%$a:-=%") else set "$az="
if "%$b:~,1%"=="-" (set "$bz=-"&set "$b=%$b:-=%") else set "$bz="
set $p=0& for /l %%n in (1,1,128) do (set/a "$_a=0,$_b=0"
if defined $a for /f "tokens=* delims=0" %%i in ("!$a:~-9!") do set/a "$_a=%$az%%%i+0"&set "$a=!$a:~,-9!"
if defined $b for /f "tokens=* delims=0" %%i in ("!$b:~-9!") do set/a "$_b=%$bz%%%i+0"&set "$b=!$b:~,-9!"
set/a "$_%%n=($_a)+($_b)+(!$p!+0)"& set/a "$p=!$_%%n:~,-9!+0"& set/a "$_%%n=!$_%%n!-(!$p!000000000)"
if "!$a!"=="" if "!$b!"=="" set/a $n=%%n+1& set "$_!$n!=!$p!"& goto:SUM_LOOP)
:SUM_LOOP
set/a "$z=0,$f=0"& for /l %%n in (%$n%,-1,1) do (if !$z! neq 0 (set/a "$k=%%n+1,$x=!$z!*($_%%n)"
set "$m=!$_%%n:-=!"& if !$x! lss 0 set/a "$f=1,$_!$k!=$_!$k!-(!$z!),$_%%n=!$z!*(1000000000-!$m:~-9!)"
) else if !$_%%n! neq 0 if "!$_%%n:~,1!"=="-" (set "$z=-1") else set "$z=+1")
if !$f! neq 0 GoTo:SUM_LOOP
if %$z% equ 0 (set "$s=0") else set "$s="& for /l %%n in (%$n%,-1,1) do (
if defined $s (set "$m=00000000!$_%%n:-=!"& set "$s=!$s!!$m:~-9!") else if !$_%%n! neq 0 set "$s=!$_%%n!")
for /f "delims=" %%i in ("%$s%") do ENDLOCAL& if "%3"=="" (echo %%i) else set "%~3=%%i"
GoTo:EOF


@echo off

set A=%RANDOM%%RANDOM%%RANDOM%%RANDOM%%RANDOM%%RANDOM%%RANDOM%%RANDOM%
set B=%RANDOM%%RANDOM%%RANDOM%%RANDOM%%RANDOM%%RANDOM%%RANDOM%%RANDOM%
set C=%RANDOM%%RANDOM%%RANDOM%%RANDOM%%RANDOM%%RANDOM%%RANDOM%%RANDOM%

call :SUM %A% %B% AB
call :SUM %B% %C% BC
call :SUM %A% %C% AC
call :SUM %AB% %BC% ABBC
call :SUM %ABBC% %AC% AABBCC

call :SUM %AABBCC% -%A% ABBCC
call :SUM %ABBCC% -%B% ABCC
call :SUM %ABCC% -%C% ABC
call :SUM %ABC% -%BC% X

echo A : %A%
echo B : %B%
echo C : %C%
echo AB : %AB%
echo BC : %BC%
echo ABBC : %ABBC%
echo AABBCC : %AABBCC%
echo ABBCC : %ABBCC%
echo ABCC : %ABCC%
echo ABC : %ABC%
echo -------:
echo A1 : %A%
echo A2 : %X%

pause>nul& exit

NiOl
08-11-2010, 22:15
amel27, примерно так и поступил, только в Вашем примере непонятен смысл "delims=0" (подозреваю, что обход ошибки автоматического преобразования из восьмеричной системы), ну да не столь важно, т.к. подумывал об использовании шестнадцатиричной системы для хранения и разбиения строки значения на части...

имхо "универсальные" варианты противоречат духу скриптов... »
согласен, но в большинстве случаев "ухищрения" вместо использования универсальной функции могут почти не дать оптимизации, а время на отладку потрачено будет значительное...

зы: впервые вопрос ограничения 32 бит возник в батнике, лепящем HTML-ки для выкладывания фоток и прочего (с уже готовыми превьюшками), там сделал процедурку для пересчета размера в "более наглядный" вид:
@Echo off
SetLocal EnableExtensions
@rem b2-20100525

set Mask=*.*
set PreView=Thumbnails
set StartUp=.

@rem Simple List - файл странички с простым списком файлов:
set SL=index.htm
@rem Advanced List - файл странички с превьюшками:
set AL=index.html

if not "%1"=="" if exist %1 set StartUp=%1

pushd %StartUp%
set Pth=%CD%
set Root=%CD%
set CurDir=.\
set DirName=.
set ExistDir=0
set SubTotal=0
set Total=0
set PVstat=0

set Deep=0
:DirLen
set /a Deep+=1
for /f "tokens=1,* delims=\" %%D in ("%Pth%") do set Pth=%%E
if not "%Pth%"=="" goto DirLen

@rem Заголовки странички
echo ^<HTML^>^<meta http-equiv="Content-Type" content="text/html;charset=cp866"^>^<BODY^>^<P align=right^>^<a href="%AL%"^>^<B^>Включить "иконки"^</B^>^</a^>^</p^> >%SL%
echo ^<HTML^>^<meta http-equiv="Content-Type" content="text/html;charset=cp866"^>^<style^>.F{text-align:center;float:left;border:4px solid #44F;margin:4px;}.N{clear:both;}A{text-decoration:none;}^</style^>^<BODY^>^<P align=right^>^<a href="%SL%"^>^<B^>Выключить "иконки"^</B^>^</a^>^</p^>>%AL%
for /f "tokens=* delims=" %%D in ('dir /a-d /s /on /b "%Mask%" 2^>nul ^| findstr /I /V /C:"\\%PreView%\\"') do call :ParseDir "%%D"
call :ShowSubTotal
rem Заключение (итого, подпись, закрывающие теги)
call :FormatSize Total

echo ^<HR^>^<B^>ИТОГО: %Total%^</B^>^<p align=right^>^<sub align=right^>Эта страничка сгенерирована автоматически скриптом. ^<I^>Nm^</I^>^</sub^>^</p^>^</BODY^>^</HTML^>>>%SL%
echo ^<HR^>^<B^>ИТОГО: %Total%^</B^>^<p align=right^>^<sub align=right^>Эта страничка сгенерирована автоматически скриптом. ^<I^>Nm^</I^>^</sub^>^</p^>^</BODY^>^</HTML^>>>%AL%
popd
goto END


:ParseDir
rem Очередной каталог/раздел?
if "%~dp1"=="%CurDir%" GoTo ParseDir1
if not "%CurDir%"==".\" (
call :ShowSubTotal
set SubTotal=0
)
set CurDir=%~dp1
for /f "tokens=%Deep%,* delims=\" %%N in ("%~dp1") do set DirName=%%O.
if "%DirName:~-2%"=="\." set DirName=%DirName:~0,-2%
set DirName=%DirName:\=/%
set ExistDir=0
if exist %CurDir%%PreView% set ExistDir=1
set PD=%PreView%
if not "%DirName%"=="." (
set PD=%DirName%/%PD%
echo ^<H3 align=center^>[%DirName%]^</H3^>>>%SL%
echo ^<H3 align=center^>[%DirName%]^</H3^>>>%AL%
)

:ParseDir1
rem ========== Отдельный файл ===========
set FN=%~nx1
if "%FN%"=="%SL%" exit /b
if "%FN%"=="%AL%" exit /b
if "%DirName%"=="." (set "FFN=%FN%") else set "FFN=%DirName%/%FN%"
set FS=%~z1
set /a SubTotal+=%FS%
call :FormatSize FS
set PVstat=0

rem Простой список
echo ^<a href="%FFN%"^>%FN%, %FS%^</a^>^<BR^>>>%SL%

rem Список с превью
echo ^<DIV class="F"^>^<a href="%FFN%"^>>>%AL%
if %ExistDir%==1 for /f "tokens=* delims=" %%F in ('dir /b /on /a-d "%CurDir%%PreView%\%~n1*.*" 2^>nul') do (
set PVstat=1
echo ^<img hspace="4" vspace="4" border="0" src="%PD%/%%~nxF"^>>>%AL%
)
if %PVstat%==0 echo ^<I^>* без иконки *^</I^>>>%AL%
echo ^<BR^>%FN%, %FS%^</a^>^</DIV^>>>%AL%
exit /b 0

rem Пересчет размера файла(ов) в краткий вид
:FormatSize
call set "NewSize=%%%1%%"
set N=0
:List1
set /a N+=1
if /i %NewSize% LSS 10000 goto List2
set /a "NewSize=(NewSize+240)>>10"
goto List1
:List2
for /f "tokens=%N%" %%E in ("B KB MB GB TB") do set %1=%NewSize%%%E
exit /b 0

rem Отображение конца блока
:ShowSubTotal
set /a Total+=%SubTotal%
call :FormatSize SubTotal
if "%DirName%"=="." (set DirName=разделу) else (set DirName=[%DirName%])
echo ^<HR^>^<B^>Всего по %DirName%: %SubTotal%^</B^>>>%SL%
echo ^<DIV class="N"^>^&nbsp;^</DIV^>^<HR^>^<B^>Всего по %DirName%: %SubTotal%^</B^>>>%AL%
exit /b 0

:END"Краткое руководство" (составлял для друзей, по чьей просьбе и создавалось):Пакетный файл для создания простого HTML-списка
графических изображений и прочих файлов.
(например для публикации в сети иНет)

Файл расположить в любом каталоге,
желательно описанным в переменной %PATH%
(см. Панель Управления / Система / Дополнительно / Переменные среды / Path)

Запуск производить из подготовленного
каталога с файлами для побликации.
Другой вариант - указать путь к каталогу
в качестве параметра, например так:
открыть 2 окна "Проводника Windows",
в одном окне должен быть файл "List2.bat",
(можно вывести ярлык на файл на рабочий стол)
а в другом - название подготовленного каталога,
после чего перетащить мышкой катлог на бат-файл.
Также можно подготавливать каталог в одном
и том же месте, а путь к нему прописать в бат-файле
в переменной "StartUp".

Подготовка каталога:
структура может быть любой, но рекомендуется
в каждой подпапке создавать каталог "Thumbnails"
(см. переменную "PreView" бат-файла), куда помещать
графические файлы (jpg/gif/png) для предварительного
просмотра. Эти файлы должны иметь начало имени файла,
совпадающее с именем (но с расширением графического файла)
публикуемого файла, к которому можно добавить некие
индексные символы, например исходный файл "Video16.rar",
файл преварительного просмотра (отображается в html-списке)
"Thumbnails\Video16.jpg" и/или "Thumbnails\Video16previw.gif"

amel27
09-11-2010, 12:50
подумывал об использовании шестнадцатиричной системы »была такая мысль, но это еще две "лишние" процедуры (DEC<->HEX), так как утилиты обычно выводят инфу в десятичном виде

непонятен смысл "delims=0" (подозреваю, что обход ошибки автоматического преобразования из восьмеричной системы) »именно так!.. к слову, пример неудачной жертвы в угоду "универсальности"... просто интересно, где может быть полезна восьмеричная запись?

NiOl
09-11-2010, 17:34
к слову, пример неудачной жертвы в угоду "универсальности"... просто интересно, где может быть полезна восьмеричная запись? »
уж не знаю, но что сделано - то сделано. :o
парочка моментов на тему автопреобразования типа данных:
1: намучался, пока отлаживал скрипт "напоминалки" - взбрело же в голову сделать фичу - если число задержки начинается с нуля - то это секунды, а иначе - минуты (ну когда 2 минуты - мало, а 3 - много ;) )
если интересно вот он: @Echo off
setlocal EnableExtensions
rem Запуск: TimerMsg [таймер] [Сообщение] * Сообщение не может начинаться с цифр, иначе будет воспринято как таймер

rem Кол-во сигналов (от 0)
set Beep=3
rem Таймер в минутах или в секундах по-дефолту, если цифра начинается с "0", например 030 - 30 секунд, 30 - 30 минут.
set Delay=5
rem Отображаемое сообщение по-дефолту
set Msg=Hurry Up!!!

set self="%~0"

if "%1"=="~" set Delay=%2 & set Msg=%~3 & goto L03

:L01
if "%1"=="" goto L02
set N=%1
if %N:~0,1%==0 set N=%N:~1%
set /a N+=0
if /i %N% gtr 0 (set Delay=%1) else set Msg=%~1
shift&goto L01

:L02
if /i %Delay:~0,1% gtr 0 (set /a Delay*=60) else set Delay=%Delay:~1%
if /i %Delay% GEQ 86400 (echo Нельзя указывать таймер более суток! &exit 1)

:L03
if /i %Delay% lss 60 (
ping 127.0.0.1 -n %Delay% 2>nul >nul
msg * /time:0 %Msg%
for /l %%n in (1,1,%Beep%) do echo  &ping 127.0.0.1 -n 1 2>nul >nul
exit
)
set /a S=%Time:~0,2% * 3600 + %Time:~3,2% * 60 + %Time:~6,2% + %Delay%
if /i %S% GEQ 86400 set /a S-=86400
set /a H=%S%/3600
set /a M=(%S%-%H%*3600)/60
set /a S=%S%-%H%*3600-%M%*60
at %H%:%M% %ComSpec% /c start "" /low /min %self% ~ %S% "%Msg%"
2: Обход преобразования из восмеричной системы я сделал так (я использовал по 9 цифр для сложения) поскольку из 10го разряда мне главное не вылезти за цифру 1 (остальные 9 разрядов могут содержать любые цифры) и использовал его как раз для "переноса", то младшая часть получалась просто: set /a Low=1%Src:~-9%-1000000000 - использовал так нелюбимое мной автопреобразование типа ;)

А вот обработать лидирующий минус - не догадался, в результате было только сложение (ну оно мне и было нужно в конкретной задаче).

amel27
10-11-2010, 06:35
я использовал по 9 цифр для сложения »это да... пожалуй, свой вариант тоже подправлю, но для умножения/деления больше 4-х цифр за раз уже не обработать :(
уж не знаю, но что сделано - то сделано. »видимо, на заре компьютеризации 8-ричное счисление было неслабо распространено, так что это рудимент ушедших времен... хотя если бы процы обрабатывали по 9 бит вместо 8, жизнь вокруг могла быть совсем иной... вот, нагуглил: Octomatics (http://www.infoverse.org/octomatics/octomatics.htm), впечатлило :)
Обход преобразования из восмеричной системы я сделал так »встречал такой приём, но он не будет короче - сначала придется либо выравнивать нулями, либо определять расположение "разряда" в числе (ведущий/нет), а так - хоть и FOR, зато всего одна операция... и не над числом, а над обычной строкой ;)

Iska
10-11-2010, 09:39
хотя если бы процы обрабатывали по 9 бит вместо 8, жизнь вокруг могла быть совсем иной... »
Угу. Вот, скажем, видел и такое: Троичный компьютер — Википедия (http://ru.wikipedia.org/wiki/%D0%A2%D1%80%D0%BE%D0%B8%D1%87%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%BC%D0%BF%D1%8C%D1%8E%D1%82%D0%B5%D1 %80).

NiOl
12-11-2010, 23:38
но для умножения/деления больше 4-х цифр за раз уже не обработать :( »
Это да, опять задумался над универсальной функцией с передачей ей строки выражения, надо будет по типу действия брать табличную величину длины кусочка... но пока все неспешно варится на подкорке - что-то нагрузили на работе всякой фигней.

...но он не будет короче - сначала придется либо выравнивать нулями, либо... »
позволю себе не согласиться. У меня было всего 2 части - младшая и старшая, полученные безусловным вырезанием из исходника. Если исходник не превышал 9 разрядов, то старшая часть оказывалась удаленной, а младшая уже не должна была содержать лидирующих нулей, соответственно сначала выделял старшую часть, проверял ее по if defined Hi ... и в условии либо получал Low по указанной выше формуле, иначе в Hi клал ноль. Для длинных цепочек можно придумать что-нить похожее.

жизнь вокруг могла быть совсем иной... вот, нагуглил: Octomatics, впечатлило :) »
Угу. Вот, скажем, видел и такое: Троичный компьютер — Википедия. »
Я в шоке - порадовало сложение в восьмеричной системе (хотя понятно, что это обычная операция "Or") и рассмешила идея в двух битах хранить всего 3 значения - действительно, цитата: троичные ЭВМ (компьютеры) имеют большую удельную ёмкость памяти :lol:

Iska
13-11-2010, 14:04
и рассмешила идея в двух битах хранить всего 3 значения - действительно, цитата: троичные ЭВМ (компьютеры) имеют большую удельную ёмкость памяти »
Стоимость разработки и построения ЭВМ в то время была весьма велика, потому, возможно, было решено использовать уже имеющуюся элементную базу.




© OSzone.net 2001-2012