]> granicus.if.org Git - php/commitdiff
Fix for bugs #68114 (Build fails on OS X due to undefined symbols)
authorKeyur Govande <keyur@php.net>
Tue, 6 Jan 2015 06:33:38 +0000 (06:33 +0000)
committerKeyur Govande <keyur@php.net>
Mon, 26 Jan 2015 21:20:29 +0000 (21:20 +0000)
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()

ext/mysqli/mysqli_api.c
ext/mysqli/tests/010.phpt
ext/mysqli/tests/bug67839.phpt
ext/mysqlnd/config9.m4
ext/mysqlnd/mysqlnd_ps_codec.c
ext/standard/config.m4
ext/standard/config.w32
ext/standard/float_to_double.c [new file with mode: 0644]
ext/standard/float_to_double.h [new file with mode: 0644]

index fd535fce396155f0ba9b9ffd14f28e37f369f332..f87776ff3d4ec4f6fe814c0d38313f2ff30286fb 100644 (file)
@@ -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);
@@ -1053,8 +1064,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
index 83a43e06b67d107a773d66409883d64db96d8d0c..b1712ca2a6464e65b2a922c4061589d8a59e3f72 100644 (file)
@@ -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!
index b2821a21bc20051fa671dfffc02fd566c8ca3158..aadce9310a53c3e9cc5fb6e7bfe943b8445a1d9b 100644 (file)
@@ -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
index 8f749cf27355d8be8e909e59af8a47b045d9c1aa..0e08b977af94db43f29c8dd6af3afd2248c70d83 100644 (file)
@@ -49,34 +49,3 @@ fi
 if test "$PHP_MYSQLND" != "no" || test "$PHP_MYSQLND_ENABLED" = "yes" || test "$PHP_MYSQLI" != "no"; then
   PHP_ADD_BUILD_DIR([ext/mysqlnd], 1)
 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
index b2dd22ba8adab914ecb4982a5c6efd2208948c3b..d2e5763210093f4318ec54a635d970107374f4cb 100644 (file)
@@ -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,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 = float_to_double(fval, (field->decimals >= NOT_FIXED_DEC) ? -1 : field->decimals);
+
        ZVAL_DOUBLE(zv, dval);
        DBG_VOID_RETURN;
 }
index 3d00d88dda15107b5503ed4c55971f3058f7144f..807f5f0b44e19e98c267a785c9f838037fe22a67 100644 (file)
@@ -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/])
index 5f24641b4d33a742986fd06214194dadd0fb9f66..525f1da422cae6e01f39cd8d733d8a2db8074d9b 100644 (file)
@@ -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")   
diff --git a/ext/standard/float_to_double.c b/ext/standard/float_to_double.c
new file mode 100644 (file)
index 0000000..5aff1fe
--- /dev/null
@@ -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
+ */
diff --git a/ext/standard/float_to_double.h b/ext/standard/float_to_double.h
new file mode 100644 (file)
index 0000000..bd70ee3
--- /dev/null
@@ -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
+ */