使用 bcmath 在 32 位系統上讀取二進位制 long

在 32 位系統上,大於 0x7FFFFFFF 的整數不能原始儲存,而 0x00000000800000000x7FFFFFFFFFFFFFFF 之間的整數可以原始儲存在 64 位系統上,而不能儲存在 32 位系統中(signed long long)。但是,由於 64 位系統和許多其他語言支援儲存 signed long long 整數,因此有時需要將此整數範圍儲存在精確值中。有幾種方法可以做到這一點,例如建立一個包含兩個數字的陣列,或者將整數轉換為十進位制的人類可讀形式。這有幾個優點,例如向使用者呈現的便利性,以及直接用 bcmath 操作它的能力。

所述 pack / unpack 方法可用於為二進位制位元組和數字的十進位制形式之間進行轉換(兩型 string 的,但一個是二進位制,一個是 ASCII),但它們將總是試圖將 ASCII 字串澆鑄成一個 32 位的在 32 位系統上的 int。以下程式碼段提供了另一種選擇:

/** Use pack("J") or pack("p") for 64-bit systems */
function writeLong(string $ascii) : string {
    if(bccomp($ascii, "0") === -1) { // if $ascii < 0
        // 18446744073709551616 is equal to (1 << 64)
        // remember to add the quotes, or the number will be parsed as a float literal
        $ascii = bcadd($ascii, "18446744073709551616");
    }

    // "n" is big-endian 16-bit unsigned short. Use "v" for small-endian.
    return pack("n", bcmod(bcdiv($ascii, "281474976710656"), "65536")) .
        pack("n", bcmod(bcdiv($ascii, "4294967296"), "65536")) .
        pack("n", bcdiv($ascii, "65536"), "65536")) .
        pack("n", bcmod($ascii, "65536"));
}

function readLong(string $binary) : string {
    $result = "0";
    $result = bcadd($result, unpack("n", substr($binary, 0, 2)));
    $result = bcmul($result, "65536");
    $result = bcadd($result, unpack("n", substr($binary, 2, 2)));
    $result = bcmul($result, "65536");
    $result = bcadd($result, unpack("n", substr($binary, 4, 2)));
    $result = bcmul($result, "65536");
    $result = bcadd($result, unpack("n", substr($binary, 6, 2)));

    // if $binary is a signed long long
    // 9223372036854775808 is equal to (1 << 63) (note that this expression actually does not work even on 64-bit systems)
    if(bccomp($result, "9223372036854775808") !== -1) { // if $result >= 9223372036854775807
        $result = bcsub($result, "18446744073709551616"); // $result -= (1 << 64)
    }
    return $result;
}