PHP/MySQLi使用准备好的语句返回错误的浮点值

PHP/MySQLi使用准备好的语句返回错误的浮点值,php,mysqli,prepared-statement,php-5.3,Php,Mysqli,Prepared Statement,Php 5.3,嗯。我在这里可能有点糊涂(这是众所周知的),但我是否在PHP5.3.x的MySQLi中发现了一个未记录的预处理语句的“特性”,或者我遗漏了一些非常基本的东西 短版本-通过MySQLi和mysqlnd驱动程序在PHP中准备的语句返回错误的浮点值 长版本 首先是一些测试数据 mysql> CREATE TABLE `t2` (`v` float(6,2) NOT NULL DEFAULT '0.00'); Query OK, 0 rows affected (0.08 sec) mysql&

嗯。我在这里可能有点糊涂(这是众所周知的),但我是否在PHP5.3.x的MySQLi中发现了一个未记录的预处理语句的“特性”,或者我遗漏了一些非常基本的东西

短版本-通过MySQLi和mysqlnd驱动程序在PHP中准备的语句返回错误的浮点值

长版本

首先是一些测试数据

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。。。查询调用时,返回正确的数据。但是,通过prepared语句使用相同的查询会返回不正确的值:

测试代码:

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;
}
如果我将表
t2
v
的类型更改为
DOUBLE
而不是
FLOAT
,则准备好的语句返回的值是正确的

在Linux机器上运行脚本(OpenSuse 12.1 32位,MySQL和PHP从源代码编译)和Windows 7(64位,PHP从二进制文件安装,数据来自Linux机器)时,我得到了相同的响应。我也在OpenSuse 12.1 64位安装(同样,PHP和MySQL是使用mysqlnd驱动程序从源代码安装的)上尝试了这一点,得到了相同的结果。所有版本都使用PHP提供的
mysqlnd
驱动程序

问题我是否错过了一些非常基本的东西,或者我应该向PHP.NET报告一个bug

抱歉,这是一个很长的问题,但我想我会提供尽可能多的数据

事实上,旧的(前两个)陈述是错误的。浮点值本质上是不精确的,例如,无法以浮点格式精确表示许多精确的小数。因此,与.03最接近的浮点值是.0299999…某个值。在数据类型转换期间,您的前两条语句正在享受MySQL驱动程序和/或PHP为您舍入值的“好处”。然而,MySQLi编写的语句使用二进制结果格式,将值“原样”直接转换为PHP数据类型,如int、float、string等()


对浮点精度问题(例如)进行更多的研究,看看它们是否真的适合您的需要。如果您想要精确的小数,并且愿意牺牲一些存储空间和/或计算速度,请尝试MySQL
DECIMAL(X,Y)
type。然而,MySQLi可能仍然会在内部将其转换为PHP float/double。

我的0.02$是因为您错过了一些非常基本的东西——float的工作方式。浮动不是用来存储任何精确的东西。这就是计算的一般方式,不仅仅是在这里。我知道浮点数从来不会精确地存储在二进制中(我已经老了,这么做已经太久了),但我很困惑,为什么我使用相同的PHP SQL驱动程序使用两个不同的查询系统得到不同的结果。不过,下面的J·米勒一针见血。谢谢你的评论。非常感谢啊。我当时很愚蠢。我现在不再疑惑,现在开始使用双值。谢谢你帮我澄清。不,不愚蠢:)--只是那些隐藏在令人激动的文档中的细微差别之一。请注意,尽管double仍然是一种浮点格式,但它们的精度是双倍的(8字节对4字节)。因此,如果它在双打中显示“.03”,那么在幕后仍然会有某种自动取整。最经得起未来考验的方法是在显示/前端代码中处理舍入。
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
)