PHP Keyfi Büyük tamsayılar ile aritmetik

4 Cevap php

Tamam, bu yüzden PHP sadece doğal, 32-bitlik imzalı tamsayı destekler düşünüyor, keyfi büyük tamsayılar ile ilgili olmak üzere en iyi dil değildir. Ne olsa yapmaya çalışıyorum (/ çıkarma / çarpma / bölme eklemek) bir keyfi büyük ikili sayısını temsil eder ve ikisi basit aritmetik işlemleri gerçekleştirmek mümkün olabilecek bir sınıf oluşturmak olduğunu.

Benim hedef 128-bit tamsayı ile uğraşıyor.

Ben bakıyorum yaklaşımlar, ve ben onlarla görmek sorunların bir çift var. Eğer bu konuda gitmek nasıl tercih ve ne herhangi bir giriş veya şerh büyük mutluluk duyacağız.

Approach #1:, dört 32-bit tamsayı olarak kendi iç tamsayı saklayan bir 128-bit tamsayı sınıf oluşturun. Bu yaklaşım ile tek sorun iki işlenen bireysel parçaları işlenirken taşma / Yetersizlik sorunları ele hakkında gitmek nasıl emin değilim ki.

Approach #2: Bu onu çözmek için tasarlanmış bir şey gibi görünüyor, BCMath uzantısı kullanın. Benim 128-bit tamsayı herhangi bir yuvarlama hataları olamaz, çünkü bu yaklaşım benim tek endişe, BCMath uzatma ölçek ayarı; onlar kesin olmalıdır. Ben de sonunda bir ikili dize (Daha sonra bazı mcrypt şifreleme fonksiyonları içine kıpırdamak gerekir ki) içine BCMath fonksiyonların sonucu dönüştürmek için güçlü olmak için endişeleniyorum.

Approach #3: (muhtemelen LSB ilk) ikili dizeleri olarak sayıları saklayın. Teorik olarak ben herhangi bir keyfi boyutta tamsayıyı bu şekilde saklamak gerekir. Ben yapmak zorunda olan tüm iki ikili dizeleri eklenti / sub / mult / div gerçekleştirmek ve bir ikili dize sonuç üretmek için dört temel aritmetik fonksiyonları yazmaya. Bu benim de Mcrypt teslim etmeye gerek tam biçimi, böylece eklenen bir artı var. Bu benim şu anda en çok söz olduğunu düşünüyorum yaklaşımdır, ama elimdeki tek anlaşmazlık noktası PHP (ben biliyorum) bana tek tek bitleri işlemek için herhangi bir yol sunmuyor olması. Ben Yaklaşım # 1 uygulamak taşma / Yetersizlik işleme hakkında soruları bu noktada, (hayır cinas amaçlanan) bayt büyüklüğünde parçalar bölmek olurdu inanıyorum.

4 Cevap

PHP GMP extension Bunun için daha iyi olacaktır. Ek bir avantaj olarak, bu nedenle gibi, onlu ikili dönüşüm yapmak için kullanabilirsiniz:

gmp_strval(gmp_init($n, 10), 2);

Çeşitli classes available Bunun için (hala gereklidir gerçekten kendi çözümü yazarken eğer) böylece kendi çözüm yazmadan önce onlara bakmak isteyebilirsiniz zaten vardır.

Bildiğim kadarıyla söyleyebilirim, BCMath uzantısı isteyeceksiniz biridir. PHP kılavuzda veriler biraz seyrek, ama dışarı bcscale () işlevi, ya da diğer BCMath fonksiyonların çoğunda opsiyonel üçüncü parametresini kullanarak ihtiyacınız tam olarak ne olduğu kesinlik ayarlamak mümkün. Çok ikili dizeleri şeye emin değilim, ama googling a bit paketi () fonksiyonu yararlanarak ile yapmak mümkün olmalı söyler değil.

Ben size yararlı olabilecek PEMDAS complaint BC evaluator aşağıdaki uygulanmaktadır.

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;
}

Bu otomatik olarak sadece ihtiyacınız ne olursa olsun haneye kadar hassas ayarlayabilirsiniz, yuvarlama hataları ile ilgilidir.