문제

좋습니다. 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;
}

반올림 오류를 자동으로 처리하므로 필요한 숫자로 정밀도를 설정하기만 하면 됩니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top