Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions ext/mysqli/mysqli_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "ext/standard/php_smart_str.h"
#include "php_mysqli_structs.h"
#include "mysqli_priv.h"
#include "ext/mysqlnd/mysql_float_to_double.h"


#if !defined(MYSQLI_USE_MYSQLND)
Expand Down Expand Up @@ -413,8 +414,18 @@ mysqli_stmt_bind_result_do_bind(MY_STMT *stmt, zval ***args, unsigned int argc,
col_type = (stmt->stmt->fields) ? stmt->stmt->fields[ofs].type : MYSQL_TYPE_STRING;

switch (col_type) {
case MYSQL_TYPE_DOUBLE:
case MYSQL_TYPE_FLOAT:
convert_to_double_ex(args[i]);
stmt->result.buf[ofs].type = IS_DOUBLE;
stmt->result.buf[ofs].buflen = sizeof(float);

stmt->result.buf[ofs].val = (char *)emalloc(sizeof(float));
bind[ofs].buffer_type = MYSQL_TYPE_FLOAT;
bind[ofs].buffer = stmt->result.buf[ofs].val;
bind[ofs].is_null = &stmt->result.is_null[ofs];
break;

case MYSQL_TYPE_DOUBLE:
convert_to_double_ex(args[i]);
stmt->result.buf[ofs].type = IS_DOUBLE;
stmt->result.buf[ofs].buflen = sizeof(double);
Expand Down Expand Up @@ -1045,8 +1056,22 @@ void mysqli_stmt_fetch_libmysql(INTERNAL_FUNCTION_PARAMETERS)
}
break;
case IS_DOUBLE:
ZVAL_DOUBLE(stmt->result.vars[i], *(double *)stmt->result.buf[i].val);
{
double dval;
if (stmt->stmt->bind[i].buffer_type == MYSQL_TYPE_FLOAT) {
#ifndef NOT_FIXED_DEC
# define NOT_FIXED_DEC 31
#endif
dval = mysql_float_to_double(*(float *)stmt->result.buf[i].val,
(stmt->stmt->fields[i].decimals >= NOT_FIXED_DEC) ? -1 :
stmt->stmt->fields[i].decimals);
} else {
dval = *((double *)stmt->result.buf[i].val);
}

ZVAL_DOUBLE(stmt->result.vars[i], dval);
break;
}
case IS_STRING:
if (stmt->stmt->bind[i].buffer_type == MYSQL_TYPE_LONGLONG
#if MYSQL_VERSION_ID > 50002
Expand Down
6 changes: 3 additions & 3 deletions ext/mysqli/tests/010.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,18 @@ mysqli_close($link);
--EXPECT--
array(7) {
[0]=>
float(3.141593)
float(3.14159)
[1]=>
float(-1.0E-6)
[2]=>
float(0)
[3]=>
float(1.0E+12)
[4]=>
float(0.5646425)
float(0.564642)
[5]=>
float(1)
[6]=>
float(8.888889E+14)
float(8.88889E+14)
}
done!
18 changes: 13 additions & 5 deletions ext/mysqli/tests/bug67839.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,27 @@ mysqli_float_handling - ensure 4 byte float is handled correctly
die();
}

if (!mysqli_stmt_execute($stmt)) {
$id = null;
$fp4 = null;
$fp8 = null;

if (!mysqli_stmt_bind_result($stmt, $id, $fp4, $fp8)) {
printf("[006] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
die();
}


if (!($result = mysqli_stmt_get_result($stmt))) {
if (!mysqli_stmt_execute($stmt)) {
printf("[007] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
die();
}

$data = mysqli_fetch_assoc($result);
print $data['id'] . ": " . $data['fp4'] . ": " . $data['fp8'] . "\n";

if (!(mysqli_stmt_fetch($stmt))) {
printf("[008] [%d] %s\n", mysqli_errno($link), mysqli_error($link));
die();
}

print $id . ": " . $fp4 . ": " . $fp8 . "\n";
?>
--CLEAN--
<?php
Expand Down
31 changes: 0 additions & 31 deletions ext/mysqlnd/config9.m4
Original file line number Diff line number Diff line change
Expand Up @@ -61,34 +61,3 @@ if test "$PHP_MYSQLND" != "no" || test "$PHP_MYSQLND_ENABLED" = "yes" || test "$
#endif
])
fi

dnl
dnl Check if the compiler supports Decimal32/64/128 types from the IEEE-754 2008 version
dnl References: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1657.pdf
dnl http://speleotrove.com/decimal/
dnl
AC_CACHE_CHECK([whether whether compiler supports Decimal32/64/128 types], ac_cv_decimal_fp_supported,[
AC_TRY_RUN( [
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv) {
typedef float dec32 __attribute__((mode(SD)));
dec32 k = 99.49f;
double d2 = (double)k;
const char *check_str = "99.49";
char print_str[32];

snprintf(print_str, 32, "%f", d2);
return memcmp(print_str, check_str, 5);
}
],[
ac_cv_decimal_fp_supported=yes
],[
ac_cv_decimal_fp_supported=no
],[
ac_cv_decimal_fp_supported=no
])])
if test "$ac_cv_decimal_fp_supported" = "yes"; then
AC_DEFINE(HAVE_DECIMAL_FP_SUPPORT, 1, [Define if the compiler supports Decimal32/64/128 types.])
fi
60 changes: 60 additions & 0 deletions ext/mysqlnd/mysql_float_to_double.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 2006-2014 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Keyur Govande <kgovande@gmail.com> |
+----------------------------------------------------------------------+
*/

#ifndef MYSQL_FLOAT_TO_DOUBLE_H
#define MYSQL_FLOAT_TO_DOUBLE_H

#include "main/php.h"
#include <float.h>
#include "main/snprintf.h"

#define MAX_CHAR_BUF_LEN 255

#ifndef FLT_DIG
# define FLT_DIG 6
#endif

/*
* Convert from a 4-byte float to a 8-byte decimal by first converting
* the float to a string, and then the string to a double.
* The decimals argument specifies the precision of the output. If decimals
* is less than zero, then a gcvt(3) like logic is used with the significant
* digits set to FLT_DIG i.e. 6.
*/
static inline double mysql_float_to_double(float fp4, int decimals) {
char num_buf[MAX_CHAR_BUF_LEN]; /* Over allocated */

if (decimals < 0) {
php_gcvt(fp4, FLT_DIG, '.', 'e', num_buf);
} else {
php_sprintf(num_buf, "%.*f", decimals, fp4);
}

return zend_strtod(num_buf, NULL);
}

/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

#endif /* MYSQL_FLOAT_TO_DOUBLE_H */
53 changes: 5 additions & 48 deletions ext/mysqlnd/mysqlnd_ps_codec.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "mysqlnd_wireprotocol.h"
#include "mysqlnd_priv.h"
#include "mysqlnd_debug.h"
#include "ext/mysqlnd/mysql_float_to_double.h"

#define MYSQLND_SILENT

Expand Down Expand Up @@ -181,56 +182,12 @@ ps_fetch_float(zval * zv, const MYSQLND_FIELD * const field, unsigned int pack_l
(*row)+= 4;
DBG_INF_FMT("value=%f", fval);

/*
* The following is needed to correctly support 4-byte floats.
* Otherwise, a value of 9.99 in a FLOAT column comes out of mysqli
* as 9.9998998641968.
*
* For GCC, we use the built-in decimal support to "up-convert" a
* 4-byte float to a 8-byte double.
* When that is not available, we fall back to converting the float
* to a string and then converting the string to a double. This mimics
* what MySQL does.
*/
#ifdef HAVE_DECIMAL_FP_SUPPORT
{
typedef float dec32 __attribute__((mode(SD)));
/* volatile so the compiler will not optimize away the conversion */
volatile dec32 d32val = fval;

/* The following cast is guaranteed to do the right thing */
dval = (double) d32val;
}
#elif defined(PHP_WIN32)
{
/* float datatype on Winows is already 4 byte but has a precision of 7 digits */
char num_buf[2048];
(void)_gcvt_s(num_buf, 2048, fval, field->decimals >= 31 ? 7 : field->decimals);
dval = zend_strtod(num_buf, NULL);
}
#else
{
char num_buf[2048]; /* Over allocated */
char *s;

#ifndef FLT_DIG
# define FLT_DIG 6
#endif
/* Convert to string. Ignoring localization, etc.
* Following MySQL's rules. If precision is undefined (NOT_FIXED_DEC i.e. 31)
* or larger than 31, the value is limited to 6 (FLT_DIG).
*/
s = php_gcvt(fval,
field->decimals >= 31 ? FLT_DIG : field->decimals,
'.',
'e',
num_buf);

/* And now convert back to double */
dval = zend_strtod(s, NULL);
}
#ifndef NOT_FIXED_DEC
# define NOT_FIXED_DEC 31
#endif

dval = mysql_float_to_double(fval, (field->decimals >= NOT_FIXED_DEC) ? -1 : field->decimals);

ZVAL_DOUBLE(zv, dval);
DBG_VOID_RETURN;
}
Expand Down