
PHP_INT_MAXぼ値を超えた数値を割ると商が少数に余りが負数になります。負の余りなんてあったっけと思いつつ調べると、定義次第とありました。
例えば、3486784400 % 7は、498112057(商)...1(余り)ですが、PHPで計算すると498112057.57143(商)...-3(余り)となります。
なぜ、商が少数となり余りが負数になるのでしょうか。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。


回答4件
0
ベストアンサー
PHPの/
は左辺と右辺が整数(int)で、かつ、割りきれる場合のみ整数(int)を返し、それ以外は浮動小数点数(float)を返します。整数商を求めたい場合はintdiv()を使用してください。しかし、3486784400 % 7
が-3
になる環境ではintdiv(3486784400, 7)
はエラーになるでしょう。それを含めて、3486784400 % 7
がなぜ-3
になるのかを説明します。
3486784400 を 7 で割った余りは -3 ?
この演算結果は算術的には全く正しくありません。確かに定義によって剰余が負の数になる場合がありますが、それは左辺と右辺の少なくとも一方が負の数になる場合のみです。両辺が正の数の場合は必ず正の数になります。勝手に負の数になることはありませんし、そのような定義もされることはありません。
なんだ、PHPは算数も出来ないのか!とお思いでしょうが、違います。正確には、PHPはある数より大きい整数については算数が出来ない、です。このとある数はPHP_INT_MAX
という定数で取得でき、OSやアーキテクチャによって固定で、通常は2147483647
(32bit環境)か9223372036854775807
(64bit環境)のどちらかです。
気付きましたか?3486784400
は2147483647
より大きい整数です。そのため、PHP_INT_MAX
が2147483647
である環境では3486784400
を整数として扱えません。そのため、整数の簡単な算数ですら、PHPは正確に出来ないのです。
では、実際どのような処理の結果-3
というとんちんかんな答えを出してしまったのかを見ていきましょう。下記処理はPHP_INT_MAX
が2147483647
の場合です。9223372036854775807
では正しく1
と答えてくれるので、該当しないことに注意してください。
まず、3486784400
というリテラル表記ですが、整数(int)ではありません。PHP_INT_MAX
を越えているため、PHPではこの表記の数を整数として扱えないからです。実際には何になるのかというと、var_dump()
で確認できます。
var_dump(3486784400); // => float(3486784400)
そう、浮動小数点数(float)です。intdiv(3486784400, 7)
がエラーになるのは、整数しか受け付けないintdiv()
に浮動小数点数(float)を入れてしまったからです。そして、困ったことにPHPでの%
の剰余計算も整数(int)同士の演算しか考慮していません。%
はエラーにならない代わりに、左辺と右辺について「整数(int)に変換する」という処理をします。7
は既に整数(int)ですので変換は起きませんが、3486784400
は浮動小数点数(float)なので、整数(int)への変換が発生します。実際、整数(int)へ変換しようとするとどうなるのでしょうか?
var_dump((int)(3486784400)); // => int(-808182896)
謎の整数-808182896
が出てきました。このようなPHP_INT_MAX
を越えている場合どのような値になるかについて、PHPマニュアルでは結果は未定義とされています。未定義だから何でもあり、では面白くないので、ソースコードを追いかけてみましょう。整数(int)変換は途中色々あるのですが、範囲超えている浮動小数点数(float)の整数(int)への変換は最終的にzend_dval_to_lval_slow()
で行われます。
ソースコード: Zend/zend_operators.c 3102行目 zend_dval_to_lval_slow()の定義(32bit) - PHP 7.2.6
やっていることは、2の32乗(4294967296
)の剰余を求めて、符号無し整数(zend_ulong)にした後、符号有り整数(zend_long)、つまり、整数(int)にキャストしています(CのdoubleがPHPのfloat、Cのzend_longがPHPのintに対応し、zend_ulongはPHP上にはあらわれない)。
3486784400
に対する4294967296
の剰余は3486784400
です。32bitの符号無し整数ではこれは表現可能ですので、このままの整数値になります。これを32bitの符号あり整数にキャストするわけですが、32bitの2の補数表現の環境では、4294967296
を引いた数、つまり、-808182896
になります。ただし、これらは環境依存であるため、全ての環境で同じような動作をするとは限らない事に注意してください。ここら辺はCをちょっと囓らないと難しいかも知れません。
とりあえず、環境によりますが、-808182896
になることはわかりました。あとは-808182896 % 7
が何になるかです。PHPの剰余の結果は左辺の符号と同じ符号で絶対値が右辺より小さくなるように求めるとなっていますので、答えは-3
となるわけです。
※ 左辺が負の数の時に右辺も負の数になるかどうかは言語によって異なります。剰余演算 - Wikipediaに各言語の結果一覧がありますので、参考にしてみてください。
投稿2018/05/15 13:30
編集2018/05/15 13:32総合スコア21749
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
なんか、WindowsじゃなくてLinux使えって事らしいですねー。
整数のサイズはプラットフォームに依存しますが、 約 20 億 (32 ビット符号付) が一般的な値です。 64 ビットプラットフォームでの通常の最大値は、およそ 9*10^18 (900京) になります。
しかし PHP 7 より前のバージョンにおける Windows は例外で、Windows で PHP 7 より前のバージョンを使う場合はは常に 32 ビットとなります。 PHP は符号無し整数をサポートしていません。
整数のサイズは定数 PHP_INT_SIZE(※注:Byte数) で、 そして整数の最大値は定数 PHP_INT_MAX でそれぞれ決まります。 これらの定数は、PHP 5.0.5 以降で使えます。 PHP 7.0.0 以降では、整数の最小値を表す定数 PHP_INT_MIN が使えるようになりました。
うそつきー! Windows XAMPP PHP7.2.3 でも PHP_INT_SIZE は 4 で PHP_INT_MAX は 2147483647 ぞ!
PHP
1<?php 2$val = 3486784400; 3echo gettype($val) . "\n"; // ← 結果 double ウェウェウェ 4``` 5 6####追記 7 8% (moduler) は左辺がINTEGERでないとダメなので double になった 3486784400 を int に暗黙キャストするんだけど、オーバーフローして値がマイナスになるんだろうな。 9 10一方、商については double をそのまま割って型は double のまま。 11 12```PHP 13<?php 14$val = 3486784400; 15echo gettype($val) . "\n"; // ← double 16echo gettype($val % 7) . "\n"; // ← integer 17echo gettype($val / 7) . "\n"; // ← double 18```
投稿2018/05/15 11:11
編集2018/05/15 11:22
退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

退会済みユーザー
2018/05/15 11:16

退会済みユーザー
2018/05/15 11:26

0
Online PHP editor | output for 0iat0
php
1<?php 2 3echo 3486784400 % 7 . PHP_EOL; 4echo 3486784400 / 7 . PHP_EOL;
質問文にあるような結果を導き出せないので、説明できません。
投稿2018/05/15 10:29

退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。