Kuvvet PHP tamsayı taşması

6 Cevap

Biz tarihsel nedenlerle birkaç statik yazdığınız dilde olduğu gibi PHP aynı çalışmak zorunda bazı tamsayı aritmetiği var. Biz geçen taşan tamsayılar için PHP davranışı yükseltti beri değişti. Temelde biz şu formülü kullanarak:

function f($x1, $x2, $x3, $x4)
{
   return (($x1 + $x2) ^ $x3) + $x4;
}

Ancak, hatta dönüşümleri ile:

function f($x1, $x2, $x3, $x4)
{
   return intval(intval(intval($x1 + $x2) ^ $x3) + $x4);
}

Ben yine tamamen yanlış numara ile biten am ...

PHP -30512150 ve C # 1617621783 Örneğin, $ x1 = -1580033017, $ x2 = -2072974554, $ x3 = -1170476976 ile) ve $ x4 = -1007518822, ben sonuna kadar.

Sadece birlikte $ x1 ve x2 $ ben doğru cevabı alınamıyor ekleyerek:

C # alıyorum

(-1580033017 + -2072974554) = 641959725

PHP:

intval(intval(-1580033017) + intval(-2072974554)) = -2147483648

: aynı olan

intval(-1580033017 + -2072974554) = -2147483648

Ben bir "IntegerOverflowAdd" fonksiyonu ya da bir şey yazmaya umursamıyorum, ama ben oldukça (+ -2072974554 -1580033017) 641959725 eşittir nasıl anlamaya olamaz. (I) o -2147483648 + (2 * 2 ^ 31 olduğunu kabul edersiniz ama ^ 31 -2147483648 + 2 peki neden ^ 31 değil, 2 ^ 31 * 2 2 eklerim edilir int.min büyüktür -1505523923 nedir?)

Herhangi bir yardım mutluluk duyacağız ...

6 Cevap

Yani sorunu çözmüş ve PHP (en azından bir şekilde bu tamsayı taşması kolları) hakkında çok şey keşfetti.

1) Bu tamamen o Suhosin Hardened PHP çalışan vardı olsun veya olmasın, makine üzerinde çalışan hangi platformu arasında bir haç, PHP sürümü bağlıydı, ve (32 veya 64) için derlenmiş kaç bit. 6 makineleri (kendi belgelerine göre en az yanlış, aslında yanlış oldu) ve ben hala izah edilemez bir şekilde davranmış 3 makineleri, ve 3 makine intval komutu yapar ne diyor göre davrandım beklenen şekilde davrandım belgeler.

2) intval int> PHP_MAX_INT (int & 0xffffffff değil), ama bu sadece PHP4 ve PHP5 bazı sürümlerinde olur PHP_MAX_INT dönmek gerekiyordu. Zaman int> PHP_MAX_INT PHP sürümleri farklı değerler döndürür.

3) Aşağıdaki kodu 3 farklı sonuçlar (bkz 1) dönebilirsiniz:

<?php
echo "Php max int: ".PHP_INT_MAX."\n";
echo "The Val: ".(-1580033017 + -2072974554)."\n";
echo "Intval of the val: ".intval(-3653007571)."\n";
echo "And 0xffffffff of the val: ".(-3653007571 & 0xffffffff)."\n";
?>

Bu (intval için doğru ama yanlış & 0xffffff olarak görünen) dönebilirsiniz

Php max int: 2147483647
The Val: -3653007571
Intval of the val: -2147483648
And of the val: -2147483648

Ve (türüne intval için PHP belgelerine çelişmektedir) dönebilirsiniz:

Php max int: 2147483647
The Val: -3653007571
Intval of the val: -641959725
And of the val: -641959725

Ve 64 Bit makinelerde bu (doğru olan) verir:

Php max int: 2147483647
The Val: -3653007571
Intval of the val: -3653007571
And of the val: -641959725

Solution

Her neyse, ben bütün bu platformlarda çalışır ve belirli bir Max int ile derlenmiş PHP belirli bir sürümü tuhaflıklar bağımlı olmaz bir çözüme ihtiyacı vardı. Böylece ben aşağıdaki çapraz-PHP thirtyTwoBitIntval fonksiyonu ile kam:

function thirtyTwoBitIntval($value)
{
    if ($value < -2147483648)
    {
        return -(-($value) & 0xffffffff);
    }
    elseif ($value > 2147483647)
    {
        return ($value & 0xffffffff);
    }
    return $value;
}

Comment

Ben PHP tasarımcıları bir Int olursa olsun (örneğin DotNet CLR gibi) 32 veya 64 veya 128 bit makine üzerinde çalışan olup olmadığını bir 32 Bit Int dedi olmalı sizce, ve rastgele bir bunu upconvert vermedi PHP altında derleyici Bit sayısına bağlı yüzer.

Eğer 32 ve 64 bit platformlarda hem intval 32-bit için% 100 çalışan bir çözüm olmasını istiyorsanız, o zaman aşağıdaki çözüm kullanmanızı öneririm:

function intval32bits($value)
{
    $value = ($value & 0xFFFFFFFF);

    if ($value & 0x80000000)
        $value = -((~$value & 0xFFFFFFFF) + 1);

    return $value;
}

Dahili olarak, PHP en numaraları için bir "tamsayı" türü kullanır. Ancak, bu sadece bugüne kadar gitmek: Büyük bir tamsayı için büyük bir tamsayı eklerseniz, PHP sonuç normal tamsayı sığmayacak kadar büyük ve bir kayan noktalı sayı atamak göreceksiniz. Kayan noktalı sayılar (yüzer) kendilerini sadece, ancak bu yüksek gidin ve PHP sadece arsa tamamen kaybedersiniz on altı haneli işareti etrafında bir nokta var.

Keyfi hassas matematik kullanmak için bir seçenek var olduğu supports numbers of any size and precision, represented as strings. Daha buraya bakın: http://us2.php.net/bc

Ben PHP varsayılan olarak 32 bit imzalanan C # gibi işaretsiz 32 bit olmak tamsayı ile yapmak zorunda düşünüyorum.

Sen, normal 31-32 bit aralığının kenarında numaraları ile oynuyor.

PHP kılavuzunda ek belgelerine bakın:

http://www.php.net/manual/en/language.types.integer.php

Yaklaşık iki milyar maksimum değer olağan değeri (32 bit imzalı bulunuyor) olmasına rağmen bir tamsayı büyüklüğü, platforma bağlıdır. PHP işaretsiz tamsayıları desteklemiyor. Tamsayı büyüklüğü PHP 4.4.0 ve PHP 5.0.5 'ten beri sürekli PHP_INT_MAX'tan kullanarak sürekli PHP_INT_SIZE ve maksimum değer kullanılarak tespit edilebilir.

Bu çalışır mı?

echo (-1580033017 + -2072974554) & 0xffffffff

Genelleştirmek için, (Ben uzun bir süre için PHP bile dokunmadım, yazım hataları pardon) yapabilirdi:

function s32add($a, $b) {
    return ($a + $b) & 0xffffffff;
}

PHP sürüm numaranızı kontrol - Ben size uzun tamsayılar için destek farklı olabilir PHP'nin farklı sürümleri ile farklı sonuçlar elde edeceksiniz mümkün olduğuna inanıyorum. Ben uzun tamsayılar bir hata ile PHP 5 sürümlerinden geçen biri olduğuna inanıyorum.

Sürümü, PHP 5.2.0 'da - cevap C # var TAM OLARAK aynı

1617621783,

Eğer yukarıda sahip tam işlevini kullanarak.

Sen kolayca, sürüm numarasını bulmak için phpinfo() komutunu kullanabilirsiniz.

$x1 = -1580033017; 
$x2 = -2072974554; 
$x3 = -1170476976 ; 
$x4 = -1007518822;
echo f($x1, $x2, $x3, $x4);

function f($x1, $x2, $x3, $x4)
{
   return intval(intval(intval($x1 + $x2) ^ $x3) + $x4);
}