문제
좋습니다. PHP는 기본적으로 32비트 부호 있는 정수만 지원한다는 점을 고려하면 임의로 큰 정수를 처리하는 데 가장 적합한 언어는 아닙니다.내가 하려는 것은 임의로 큰 이진수를 표현할 수 있고 그 중 두 개에 대해 간단한 산술 연산(더하기/빼기/곱하기/나누기)을 수행할 수 있는 클래스를 만드는 것입니다.
내 목표는 128비트 정수를 다루는 것입니다.
제가 보고 있는 몇 가지 접근 방식과 그 접근 방식에서 발견되는 문제가 있습니다.무엇을 선택하고 어떻게 진행할 것인지에 대한 의견이나 논평을 주시면 매우 감사하겠습니다.
접근법 #1: 내부적으로 정수를 4개의 32비트 정수로 저장하는 128비트 정수 클래스를 만듭니다.이 접근 방식의 유일한 문제점은 두 피연산자의 개별 청크를 조작할 때 오버플로/언더플로 문제를 처리하는 방법을 잘 모르겠다는 것입니다.
접근법 #2: bcmath 확장을 사용하세요. 이는 해결하기 위해 설계된 것처럼 보입니다.이 접근 방식을 취할 때 내가 걱정하는 유일한 것은 bcmath 확장의 크기 설정입니다. 왜냐하면 128비트 정수에는 반올림 오류가 있을 수 없기 때문입니다.정확해야 합니다.또한 bcmath 함수의 결과를 결국 이진 문자열(나중에 일부 mcrypt 암호화 함수에 넣어야 함)로 변환할 수 있을지 걱정됩니다.
접근법 #3: 숫자를 이진 문자열(아마도 LSB 먼저)로 저장합니다.이론적으로는 이런 방식으로 임의 크기의 정수를 저장할 수 있어야 합니다.내가 해야 할 일은 두 개의 이진 문자열에 대해 add/sub/mult/div를 수행하고 이진 문자열 결과를 생성하는 네 가지 기본 산술 함수를 작성하는 것뿐입니다.이것은 mcrypt에도 전달해야 하는 형식이므로 추가 장점입니다.이것이 현재로서는 가장 유망하다고 생각되는 접근 방식이지만, 한 가지 난점은 PHP가 (내가 알고 있는) 개별 비트를 조작할 수 있는 방법을 제공하지 않는다는 것입니다.나는 그것을 바이트 크기의 덩어리로 나누어야 한다고 생각합니다(말장난 의도는 아님). 이 시점에서 접근 방식 #1의 오버플로/언더플로 처리에 대한 질문이 적용됩니다.
해결책
그만큼 PHP GMP 확장 이것 때문에 더 좋을 것입니다.추가 보너스로 다음과 같이 10진수를 2진수로 변환하는 데 사용할 수 있습니다.
gmp_strval(gmp_init($n, 10), 2);
다른 팁
내가 아는 한, bcmath 확장은 여러분이 원하는 확장입니다.PHP 매뉴얼의 데이터는 약간 드물지만 bcscale() 함수를 사용하거나 대부분의 다른 bcmath 함수에서 선택적인 세 번째 매개변수를 사용하여 정확히 필요한 정밀도를 설정할 수 있습니다.바이너리 문자열에 대해서는 잘 모르겠지만, 인터넷 검색을 좀 해보면 pack() 함수를 사용하여 작업을 수행할 수 있다는 것을 알 수 있습니다.
나는 다음을 구현했다 PEMDAS 불만사항 BC 평가자 그것은 당신에게 유용할 수 있습니다.
function BC($string, $precision = 32)
{
if (extension_loaded('bcmath') === true)
{
if (is_array($string) === true)
{
if ((count($string = array_slice($string, 1)) == 3) && (bcscale($precision) === true))
{
$callback = array('^' => 'pow', '*' => 'mul', '/' => 'div', '%' => 'mod', '+' => 'add', '-' => 'sub');
if (array_key_exists($operator = current(array_splice($string, 1, 1)), $callback) === true)
{
$x = 1;
$result = @call_user_func_array('bc' . $callback[$operator], $string);
if ((strcmp('^', $operator) === 0) && (($i = fmod(array_pop($string), 1)) > 0))
{
$y = BC(sprintf('((%1$s * %2$s ^ (1 - %3$s)) / %3$s) - (%2$s / %3$s) + %2$s', $string = array_shift($string), $x, $i = pow($i, -1)));
do
{
$x = $y;
$y = BC(sprintf('((%1$s * %2$s ^ (1 - %3$s)) / %3$s) - (%2$s / %3$s) + %2$s', $string, $x, $i));
}
while (BC(sprintf('%s > %s', $x, $y)));
}
if (strpos($result = bcmul($x, $result), '.') !== false)
{
$result = rtrim(rtrim($result, '0'), '.');
if (preg_match(sprintf('~[.][9]{%u}$~', $precision), $result) > 0)
{
$result = bcadd($result, (strncmp('-', $result, 1) === 0) ? -1 : 1, 0);
}
else if (preg_match(sprintf('~[.][0]{%u}[1]$~', $precision - 1), $result) > 0)
{
$result = bcmul($result, 1, 0);
}
}
return $result;
}
return intval(version_compare(call_user_func_array('bccomp', $string), 0, $operator));
}
$string = array_shift($string);
}
$string = str_replace(' ', '', str_ireplace('e', ' * 10 ^ ', $string));
while (preg_match('~[(]([^()]++)[)]~', $string) > 0)
{
$string = preg_replace_callback('~[(]([^()]++)[)]~', __FUNCTION__, $string);
}
foreach (array('\^', '[\*/%]', '[\+-]', '[<>]=?|={1,2}') as $operator)
{
while (preg_match(sprintf('~(?<![0-9])(%1$s)(%2$s)(%1$s)~', '[+-]?(?:[0-9]++(?:[.][0-9]*+)?|[.][0-9]++)', $operator), $string) > 0)
{
$string = preg_replace_callback(sprintf('~(?<![0-9])(%1$s)(%2$s)(%1$s)~', '[+-]?(?:[0-9]++(?:[.][0-9]*+)?|[.][0-9]++)', $operator), __FUNCTION__, $string, 1);
}
}
}
return (preg_match('~^[+-]?[0-9]++(?:[.][0-9]++)?$~', $string) > 0) ? $string : false;
}
반올림 오류를 자동으로 처리하므로 필요한 숫자로 정밀도를 설정하기만 하면 됩니다.