PHP は、そのような機能では必然的に、数値を保存するために内部で zval を使用します。 zval の構造は次のとおりです (例として 5.2):
コードをコピーします コードは次のとおりです:
struct _zval_struct {
/* 変数情報 */
zvalue_value 値; /* 値 */
zend_uint refcount;
zend_uchar タイプ; /* アクティブなタイプ */
zend_uchar is_ref;
};
上記の構造では、実際に値自体を保存するのは zvalue_value ユニオンです:
コードをコピーします コードは次のとおりです:
typedef Union _zvalue_value {
長い lval; /* 長い値 */
double dval; /* 倍精度の値 */
構造体{
char *val;
int len;
} str;
HashTable *ht; /* ハッシュ テーブルの値 */
zend_object_value obj;
zvalue_value;
今日のトピックでは、そのうちの 2 つ、lval と dval にのみ焦点を当てます。long lval はコンパイラと OS のワード長によって不定であることを認識しなければなりません。また、double dval (倍精度) が指定されています。 IEEE 754 によるもので、固定長であり、64 ビットである必要があります。
これにより、一部の PHP コードが「非プラットフォーム非依存」になるため、以下の説明では、特に指定がない限り、long が 64 ビットであることを前提としています。
ここでは IEEE 754 の浮動小数点カウント方法を引用しません。興味があれば、ご自身で確認してください。重要な点は、倍精度浮動小数点の仮数が 52 ビットで格納されることです。ビット、合計は 53 ビットです。ここで、非常に興味深い疑問が生じます。例として C コードを使用してみましょう (long が 64 ビットであると仮定します)。
コードをコピーします コードは次のとおりです:
長い a = x;
assert(a == (long)(double)a);
すみません、a の値がどの範囲内にある場合、上記のコードは成功すると言えますか? (答えは記事の最後に残しておきます)
それでは、本題に戻りましょう。PHP は、スクリプトを実行する前に、まずスクリプトを読み取り、スクリプトを分析する必要があります。たとえば、次のスクリプトの場合は、次のとおりです。
コードをコピーします
コードは次のとおりです:
$a = 9223372036854775807; //64ビット符号付き数値の最大値
$b = 9223372036854775808; //最大値+1
;
var_dump($a);
var_dump($b);
出力:
コードをコピーします
コードは次のとおりです:
int(9223372036854775807)
float(9.22337203685E+18)
つまり、字句解析フェーズ中に、PHP はリテラル値が現在のシステムの長いテーブル値の範囲を超えているかどうかを判断し、超えていない場合は lval を使用して保存し、そうでない場合は dval を使用します。使用されるのは、zval IS_FLOAT を意味します。
最大の整数値より大きい値を使用すると、精度が失われる可能性があるため、注意する必要があります。
コードをコピーします
コードは次のとおりです:
$a = 9223372036854775807;
$b = 9223372036854775808;
var_dump($a === ($b - 1));
出力は false です。
冒頭の議論の続きですが、前述したように、PHP の整数は 32 ビットまたは 64 ビットであるため、64 ビットで正常に実行できる一部のコードは、目に見えない型変換、精度の損失により失敗する可能性があると判断されています。が発生すると、コードが 32 ビット システムで適切に実行されなくなります。
コードは次のとおりです:
エコー PHP_INT_MAX;
?>
もちろん、安全を期すために、文字列を使用して大きな整数を保存し、bcmath などの数学関数ライブラリを使用して計算を実行する必要があります。
さらに、私たちを混乱させるもう 1 つの重要な設定があります。この設定は php.precision です。この設定は、PHP が float 値を出力するときに出力する有効桁数を決定します。
最後に、上で挙げた質問を振り返ってみましょう。つまり、float に変換してから long に戻すときに精度が失われないようにするための、long 整数の最大値はいくらですか?
たとえば、整数の場合、そのバイナリ表現は 101 であることがわかります。次に、2 ビットを右シフトして 1.01 にし、上位の暗黙的な有効ビット 1 を破棄して、バイナリ値 5 を取得します。二重に:
コードをコピーします コードは次のとおりです:
0/*符号ビット*/ 10000000001/*指数ビット*/ 010000000000000000000000000000000000000000000000000
5 のバイナリ表現は、損失なく仮数部に格納されます。この場合、double から long に変換するときに精度が失われることはありません。
コードをコピーします コードは次のとおりです:
2^53 - 1 == 9007199254740991; //ここでは 64 ビット長であると仮定していることに注意してください
そうすれば、long->double->long 値の変換が発生したときに、この整数の精度が失われることはありません。
http://www.bkjia.com/PHPjc/998815.html