使用 bcmath 在 32 位系統上讀取二進位制 long
在 32 位系統上,大於 0x7FFFFFFF
的整數不能原始儲存,而 0x0000000080000000
和 0x7FFFFFFFFFFFFFFF
之間的整數可以原始儲存在 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;
}