3

わかった。私はここで太っているかもしれませんが(それは知られています)、PHP 5.3.xのMySQLiでプリペアドステートメントの文書化されていない「機能」を見つけたか、かなり基本的なものを見逃しました。

短いバージョン-MySQLiおよびmysqlndドライバーを介してPHPでプリペアドステートメントが誤ったfloat値を返す

ロングバージョン

まず第一に、いくつかのテストデータ

mysql> CREATE TABLE `t2` (`v` float(6,2) NOT NULL DEFAULT '0.00');
Query OK, 0 rows affected (0.08 sec)

mysql> insert into t2 (v) values (0.03);
Query OK, 1 row affected (0.00 sec)

プリペアドステートメントを介して上記の値を取得すると、問題が発生します。従来のmysql_...または標準のmysqli...クエリ呼び出しを使用すると、正しいデータが返されます。ただし、プリペアドステートメントを介して同じクエリを使用すると、誤った値が返されます。

テストコード:

atom:~/testScripts> cat f1.php
<?php
require('passwds.php');

$q="select * from t2"; // same query for all

echo "/* Old style MySQL statements (deprecated) */".PHP_EOL;

$h1=mysql_connect(MYSQL_SERVER,MYSQL_USER,MYSQL_PASSWORD);
mysql_select_db('test',$h1);
$r1=mysql_query($q);
while ($f1=mysql_fetch_assoc($r1))
{
    echo print_r($f1,true).PHP_EOL;
}

echo "/* New style MySQLi statements */".PHP_EOL;

$h2=new mysqli(MYSQL_SERVER,MYSQL_USER,MYSQL_PASSWORD,'test');
$r2=$h2->query($q);
while ($f2=$r2->fetch_assoc())
{
    echo print_r($f2,true).PHP_EOL;
}

echo "/* New style MySQLi prepared statements */".PHP_EOL;

$h3=new mysqli(MYSQL_SERVER,MYSQL_USER,MYSQL_PASSWORD,'test');
$s3=$h3->stmt_init();
$s3->prepare($q);
$s3->execute(); // no binding required
$r3=$s3->get_result();
while ($f3=$r3->fetch_assoc())
{
    echo print_r($f3,true).PHP_EOL;
}

結果:

atom:~/testScripts> php -f f1.php
/* Old style MySQL statements (deprecated) */
Array
(
    [v] => 0.03
)

/* New style MySQLi statements */
Array
(
    [v] => 0.03
)

/* New style MySQLi prepared statements */
Array
(
    [v] => 0.029999999329448
)

vinテーブルのタイプをではなくに変更するとt2DOUBLEプリペアドFLOATステートメントによって返される値は正しいです。

Linuxボックス(ソースからコンパイルされたMySQLとPHPを使用したOpenSuse 12.1 32ビット)とWindows 7(Linuxマシンからのデータを含むバイナリからインストールされたPHPを使用した64ビット)でスクリプトを実行すると、同じ応答が返されます。OpenSuse 12.1 64ビットインストール(ここでも、mysqlndドライバーを使用してソースからインストールされたPHPとMySQL)でこれを試しましたが、同じ結果になりました。すべてのバージョンはmysqlnd、PHPで提供されるドライバーを使用しています

質問かなり基本的なことを見逃したことがありますか、それともPHP.NETにバグを報告する必要がありますか?

長い質問ですが、できるだけ多くのデータを提供したいと思いました。

4

1 に答える 1

2

間違っているのは、実際には古い(最初の2つの)ステートメントです。浮動小数点値は本質的に不正確です。たとえば、多くの正確な小数を浮動小数点形式で正確に表す方法はありません。したがって、.03に最も近い浮動小数点値は.0299999...何かです。最初の2つのステートメントは、MySQLドライバーの「利点」や、データ型変換中に値を丸めるPHPを楽しんでいます。ただし、MySQLiのプリペアドステートメントは、値を「現状のまま」、int、float、stringなどのPHPデータ型に直接変換するバイナリ結果形式を使用します(http://php.net/manual/en/mysqli.quickstart.prepared- statement.php

浮動小数点の精度の問題(http://dev.mysql.com/doc/refman/5.5/en/problems-with-float.htmlなど)についてさらに調査を行い、それらが本当にニーズに合っているかどうかを確認します。正確な小数が必要で、ストレージスペースや計算速度を少し犠牲にしても構わないと思っている場合は、MySQLDECIMAL(X,Y)タイプを試してください。ただし、MySQLiはこれを内部でPHP float/doubleに変換する場合があります。

于 2012-07-18T19:05:26.983 に答える