mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Fix for bugs #68114 (Build fails on OS X due to undefined symbols)
and #68657 (Reading 4 byte floats with Mysqli and libmysqlclient has rounding errors). The patch removes support for Decimal floating point numbers and now defaults to using similar logic as what libmysqlclient does: convert a 4 byte floating point number into a string, and then the string into a double. The quirks of MySQL are maintained as seen in Field_Float::val_str()
This commit is contained in:
parent
3f1d1892c9
commit
f2eadb93b9
9 changed files with 140 additions and 90 deletions
|
@ -33,6 +33,7 @@
|
|||
#include "ext/standard/php_smart_str.h"
|
||||
#include "php_mysqli_structs.h"
|
||||
#include "mysqli_priv.h"
|
||||
#include "ext/standard/float_to_double.h"
|
||||
|
||||
|
||||
#if !defined(MYSQLI_USE_MYSQLND)
|
||||
|
@ -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);
|
||||
|
@ -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 = 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
|
||||
|
|
|
@ -62,7 +62,7 @@ mysqli_close($link);
|
|||
--EXPECT--
|
||||
array(7) {
|
||||
[0]=>
|
||||
float(3.141593)
|
||||
float(3.14159)
|
||||
[1]=>
|
||||
float(-1.0E-6)
|
||||
[2]=>
|
||||
|
@ -70,10 +70,10 @@ array(7) {
|
|||
[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!
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "mysqlnd_wireprotocol.h"
|
||||
#include "mysqlnd_priv.h"
|
||||
#include "mysqlnd_debug.h"
|
||||
#include "ext/standard/float_to_double.h"
|
||||
|
||||
#define MYSQLND_SILENT
|
||||
|
||||
|
@ -181,55 +182,11 @@ 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
|
||||
#ifndef NOT_FIXED_DEC
|
||||
# define NOT_FIXED_DEC 31
|
||||
#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);
|
||||
}
|
||||
#endif
|
||||
dval = float_to_double(fval, (field->decimals >= NOT_FIXED_DEC) ? -1 : field->decimals);
|
||||
|
||||
ZVAL_DOUBLE(zv, dval);
|
||||
DBG_VOID_RETURN;
|
||||
|
|
|
@ -603,7 +603,8 @@ PHP_NEW_EXTENSION(standard, array.c base64.c basic_functions.c browscap.c crc32.
|
|||
incomplete_class.c url_scanner_ex.c ftp_fopen_wrapper.c \
|
||||
http_fopen_wrapper.c php_fopen_wrapper.c credits.c css.c \
|
||||
var_unserializer.c ftok.c sha1.c user_filters.c uuencode.c \
|
||||
filters.c proc_open.c streamsfuncs.c http.c password.c)
|
||||
filters.c proc_open.c streamsfuncs.c http.c password.c \
|
||||
float_to_double.c)
|
||||
|
||||
PHP_ADD_MAKEFILE_FRAGMENT
|
||||
PHP_INSTALL_HEADERS([ext/standard/])
|
||||
|
|
|
@ -20,7 +20,7 @@ EXTENSION("standard", "array.c base64.c basic_functions.c browscap.c \
|
|||
url_scanner_ex.c ftp_fopen_wrapper.c http_fopen_wrapper.c \
|
||||
php_fopen_wrapper.c credits.c css.c var_unserializer.c ftok.c sha1.c \
|
||||
user_filters.c uuencode.c filters.c proc_open.c password.c \
|
||||
streamsfuncs.c http.c flock_compat.c", false /* never shared */);
|
||||
streamsfuncs.c http.c flock_compat.c float_to_double.c", false /* never shared */);
|
||||
PHP_INSTALL_HEADERS("", "ext/standard");
|
||||
if (PHP_MBREGEX != "no") {
|
||||
CHECK_HEADER_ADD_INCLUDE("oniguruma.h", "CFLAGS_STANDARD", PHP_MBREGEX + ";ext\\mbstring\\oniguruma")
|
||||
|
|
55
ext/standard/float_to_double.c
Normal file
55
ext/standard/float_to_double.c
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| 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> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include <float.h>
|
||||
#include "float_to_double.h"
|
||||
#include "main/snprintf.h"
|
||||
|
||||
#define MAX_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.
|
||||
*/
|
||||
double float_to_double(float fp4, int decimals) {
|
||||
char num_buf[MAX_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
|
||||
*/
|
35
ext/standard/float_to_double.h
Normal file
35
ext/standard/float_to_double.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| 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 FLOAT_TO_DOUBLE_H
|
||||
#define FLOAT_TO_DOUBLE_H
|
||||
|
||||
#include "main/php.h"
|
||||
|
||||
PHPAPI double float_to_double(float fp4, int decimals);
|
||||
|
||||
#endif /* FLOAT_TO_DOUBLE_H */
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
Loading…
Add table
Add a link
Reference in a new issue