]> granicus.if.org Git - php/commitdiff
- MFH: Added the H modifier to create non-locale-aware non-fixed-precision
authorDerick Rethans <derick@php.net>
Tue, 19 Jun 2007 12:20:50 +0000 (12:20 +0000)
committerDerick Rethans <derick@php.net>
Tue, 19 Jun 2007 12:20:50 +0000 (12:20 +0000)
  float representations.
- MFH: Fixed var_export() to use the new H modifier so that it can generate
  parsable PHP code for floats again, independent of the locale.

NEWS
ext/standard/tests/general_functions/var_export-locale.phpt [new file with mode: 0644]
ext/standard/var.c
main/snprintf.c
main/spprintf.c

diff --git a/NEWS b/NEWS
index cfe741898d6e2329486b80f0071401bdb92c8c2c..81c44077ffe9983803821ae4b9d5a2ff72375c3c 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -19,6 +19,8 @@ PHP                                                                        NEWS
 - Added missing format validator to unpack() function. (Ilia)
 - Added missing error check inside bcpowmod(). (Ilia)
 
+- Fixed var_export() to use the new H modifier so that it can generate
+  parsable PHP code for floats again, independent of the locale.
 - Fixed regression introduced by the fix for the libgd bug #74 (Pierre)
 - Fixed several integer overflows in ImageCreate(), ImageCreateTrueColor(), 
   ImageCopyResampled() and ImageFilledPolygon() reported by Mattias Bengtsson. 
diff --git a/ext/standard/tests/general_functions/var_export-locale.phpt b/ext/standard/tests/general_functions/var_export-locale.phpt
new file mode 100644 (file)
index 0000000..6d2df85
--- /dev/null
@@ -0,0 +1,1061 @@
+--TEST--
+Test var_export() function with locale
+--INI--
+precision=14
+--SKIPIF--
+<?php
+if (!setlocale(LC_ALL, "de","de_DE","de_DE.ISO8859-1","de_DE.ISO_8859-1","de_DE.UTF-8")) {
+        die("skip locale needed for this test is not supported on this platform");
+}
+?>
+--FILE--
+<?php
+setlocale(LC_ALL, "de","de_DE","de_DE.ISO8859-1","de_DE.ISO_8859-1","de_DE.UTF-8");
+/* Prototype: mixed var_export( mixed expression [, bool return]);
+ * Description: Returns the variable representation when the return parameter is used and evaluates to TRUE. Otherwise, this function will return NULL.
+
+*/
+
+echo "*** Testing var_export() with integer values ***\n";
+// different integer vlaues 
+$valid_ints = array(
+                '0',
+                '1',
+                '-1',
+                '-2147483648', // max negative integer value
+                '-2147483647', 
+                2147483647,  // max positive integer value
+                2147483640,
+                0x123B,      // integer as hexadecimal
+                '0x12ab',
+                '0Xfff',
+                '0XFA',
+                -0x80000000, // max negative integer as hexadecimal
+                '0x7fffffff',  // max postive integer as hexadecimal
+                0x7FFFFFFF,  // max postive integer as hexadecimal
+                '0123',        // integer as octal
+                01912,       // should be quivalent to octal 1
+                -020000000000, // max negative integer as octal
+                017777777777,  // max positive integer as octal
+               );
+$counter = 1;
+/* Loop to check for above integer values with var_export() */
+echo "\n*** Output for integer values ***\n";
+foreach($valid_ints as $int_value) {
+echo "\nIteration ".$counter."\n";
+var_export( $int_value );
+echo "\n";
+var_export( $int_value, FALSE);
+echo "\n";
+var_dump( var_export( $int_value, TRUE) );
+echo "\n";
+$counter++;
+}
+
+echo "*** Testing var_export() with valid boolean values ***\n";
+// different valid  boolean vlaues 
+$valid_bool = array(
+                   1,
+                   TRUE,
+                true, 
+                0,
+                   FALSE,
+                   false
+               );
+$counter = 1;
+/* Loop to check for above boolean values with var_export() */
+echo "\n*** Output for boolean values ***\n";
+foreach($valid_bool as $bool_value) {
+echo "\nIteration ".$counter."\n";
+var_export( $bool_value );
+echo "\n";
+var_export( $bool_value, FALSE);
+echo "\n";
+var_dump( var_export( $bool_value, TRUE) );
+echo "\n";
+$counter++;
+}
+
+echo "*** Testing var_export() with valid float values ***\n";
+// different valid  float vlaues 
+$valid_floats = array(
+  -2147483649, // float value
+  2147483648,  // float value
+  -0x80000001, // float value, beyond max negative int
+  0x800000001, // float value, beyond max positive int
+  020000000001, // float value, beyond max positive int
+  -020000000001, // float value, beyond max negative int
+  0.0,
+  -0.1,
+  10.0000000000000000005,
+  10.5e+5,
+  1e5,
+  1e-5,
+  1e+5,
+  1E5,
+  1E+5,
+  1E-5,
+  .5e+7,
+  .6e-19,
+  .05E+44,
+  .0034E-30
+);
+$counter = 1;
+/* Loop to check for above float values with var_export() */
+echo "\n*** Output for float values ***\n";
+foreach($valid_bool as $float_value) {
+echo "\nIteration ".$counter."\n";
+var_export( $float_value );
+echo "\n";
+var_export( $float_value, FALSE);
+echo "\n";
+var_dump( var_export( $float_value, TRUE) );
+echo "\n";
+$counter++;
+}
+
+echo "*** Testing var_export() with valid strings ***\n";
+// different valid  string 
+$valid_strings = array(
+            "",
+            " ",
+            '',
+            ' ',
+            "string",
+            'string',
+            "NULL",
+            'null',
+            "FALSE",
+            'false',
+            "\x0b",
+            "\0",
+            '\0',
+            '\060',
+            "\070"
+          );
+$counter = 1;
+/* Loop to check for above strings with var_export() */
+echo "\n*** Output for strings ***\n";
+foreach($valid_strings as $str) {
+echo "\nIteration ".$counter."\n";
+var_export( $str );
+echo "\n";
+var_export( $str, FALSE);
+echo "\n";
+var_dump( var_export( $str, TRUE) );
+echo "\n";
+$counter++;
+}
+
+echo "*** Testing var_export() with valid arrays ***\n";
+// different valid  arrays 
+$valid_arrays = array(
+           array(),
+           array(NULL),
+           array(null),
+           array(true),
+           array(""),
+           array(''),
+           array(array(), array()),
+           array(array(1, 2), array('a', 'b')),
+           array(1 => 'One'),
+           array("test" => "is_array"),
+           array(0),
+           array(-1),
+           array(10.5, 5.6),
+           array("string", "test"),
+           array('string', 'test')
+          );
+$counter = 1;
+/* Loop to check for above arrays with var_export() */
+echo "\n*** Output for arrays ***\n";
+foreach($valid_arrays as $arr) {
+echo "\nIteration ".$counter."\n";
+var_export( $arr );
+echo "\n";
+var_export( $arr, FALSE);
+echo "\n";
+var_dump( var_export( $arr, TRUE) );
+echo "\n";
+$counter++;
+}
+
+echo "*** Testing var_export() with valid objects ***\n";
+
+// class with no members
+class foo
+{
+// no members 
+}
+
+// abstract class
+abstract class abstractClass
+{
+  abstract protected function getClassName();
+  public function printClassName () {
+    echo $this->getClassName() . "\n";
+  }
+}
+// implement abstract class
+class concreteClass extends abstractClass
+{
+  protected function getClassName() {
+    return "concreteClass";
+  }
+}
+
+// interface class 
+interface iValue
+{
+   public function setVal ($name, $val); 
+   public function dumpVal ();
+}
+// implement the interface
+class Value implements iValue
+{
+  private $vars = array ();
+  
+  public function setVal ( $name, $val ) {
+    $this->vars[$name] = $val;
+  }
+  
+  public function dumpVal () {
+    var_export ( $vars );
+  }
+}
+
+// a gereral class 
+class myClass 
+{
+  var $foo_object;
+  public $public_var;
+  public $public_var1;
+  private $private_var;
+  protected $protected_var;
+
+  function myClass ( ) {
+    $this->foo_object = new foo();
+    $this->public_var = 10;
+    $this->public_var1 = new foo();
+    $this->private_var = new foo();
+    $this->proected_var = new foo();
+  }  
+}
+
+// create a object of each class defined above
+$myClass_object = new myClass();
+$foo_object = new foo();
+$Value_object = new Value();
+$concreteClass_object = new concreteClass();
+
+$valid_objects = array(
+                  new stdclass,
+                  new foo,
+                  new concreteClass,
+                  new Value,
+                  new myClass,
+                  $myClass_object,
+                  $myClass_object->foo_object,
+                  $myClass_object->public_var1,
+                  $foo_object,
+                  $Value_object,
+                  $concreteClass_object
+                 ); 
+ $counter = 1;
+/* Loop to check for above objects with var_export() */
+echo "\n*** Output for objects ***\n";
+foreach($valid_objects as $obj) {
+echo "\nIteration ".$counter."\n";
+var_export( $obj );
+echo "\n";
+var_export( $obj, FALSE);
+echo "\n";
+var_dump( var_export( $obj, TRUE) );
+echo "\n";
+$counter++;
+}
+                 
+echo "*** Testing var_export() with valid null values ***\n";
+// different valid  null vlaues 
+$unset_var = array();
+unset ($unset_var); // now a null
+$null_var = NULL;
+
+$valid_nulls = array(
+                NULL,
+                null,
+                $null_var,
+               );
+ $counter = 1;
+/* Loop to check for above null values with var_export() */
+echo "\n*** Output for null values ***\n";
+foreach($valid_nulls as $null_value) {
+echo "\nIteration ".$counter."\n";
+var_export( $null_value );
+echo "\n";
+var_export( $null_value, FALSE);
+echo "\n";
+var_dump( var_export( $null_value, true) );
+echo "\n";
+$counter++;
+}
+
+echo "\n*** Testing error conditions ***\n";
+//Zero argument
+var_export( var_export() );
+
+//arguments more than expected 
+var_export( var_export(TRUE, FALSE, TRUE) );
+echo "\n\nDone";
+
+
+?>
+--EXPECTF--
+*** Testing var_export() with integer values ***
+
+*** Output for integer values ***
+
+Iteration 1
+'0'
+'0'
+string(3) "'0'"
+
+
+Iteration 2
+'1'
+'1'
+string(3) "'1'"
+
+
+Iteration 3
+'-1'
+'-1'
+string(4) "'-1'"
+
+
+Iteration 4
+'-2147483648'
+'-2147483648'
+string(13) "'-2147483648'"
+
+
+Iteration 5
+'-2147483647'
+'-2147483647'
+string(13) "'-2147483647'"
+
+
+Iteration 6
+2147483647
+2147483647
+string(10) "2147483647"
+
+
+Iteration 7
+2147483640
+2147483640
+string(10) "2147483640"
+
+
+Iteration 8
+4667
+4667
+string(4) "4667"
+
+
+Iteration 9
+'0x12ab'
+'0x12ab'
+string(8) "'0x12ab'"
+
+
+Iteration 10
+'0Xfff'
+'0Xfff'
+string(7) "'0Xfff'"
+
+
+Iteration 11
+'0XFA'
+'0XFA'
+string(6) "'0XFA'"
+
+
+Iteration 12
+-2147483648
+-2147483648
+string(11) "-2147483648"
+
+
+Iteration 13
+'0x7fffffff'
+'0x7fffffff'
+string(12) "'0x7fffffff'"
+
+
+Iteration 14
+2147483647
+2147483647
+string(10) "2147483647"
+
+
+Iteration 15
+'0123'
+'0123'
+string(6) "'0123'"
+
+
+Iteration 16
+1
+1
+string(1) "1"
+
+
+Iteration 17
+-2147483648
+-2147483648
+string(11) "-2147483648"
+
+
+Iteration 18
+2147483647
+2147483647
+string(10) "2147483647"
+
+*** Testing var_export() with valid boolean values ***
+
+*** Output for boolean values ***
+
+Iteration 1
+1
+1
+string(1) "1"
+
+
+Iteration 2
+true
+true
+string(4) "true"
+
+
+Iteration 3
+true
+true
+string(4) "true"
+
+
+Iteration 4
+0
+0
+string(1) "0"
+
+
+Iteration 5
+false
+false
+string(5) "false"
+
+
+Iteration 6
+false
+false
+string(5) "false"
+
+*** Testing var_export() with valid float values ***
+
+*** Output for float values ***
+
+Iteration 1
+1
+1
+string(1) "1"
+
+
+Iteration 2
+true
+true
+string(4) "true"
+
+
+Iteration 3
+true
+true
+string(4) "true"
+
+
+Iteration 4
+0
+0
+string(1) "0"
+
+
+Iteration 5
+false
+false
+string(5) "false"
+
+
+Iteration 6
+false
+false
+string(5) "false"
+
+*** Testing var_export() with valid strings ***
+
+*** Output for strings ***
+
+Iteration 1
+''
+''
+string(2) "''"
+
+
+Iteration 2
+' '
+' '
+string(3) "' '"
+
+
+Iteration 3
+''
+''
+string(2) "''"
+
+
+Iteration 4
+' '
+' '
+string(3) "' '"
+
+
+Iteration 5
+'string'
+'string'
+string(8) "'string'"
+
+
+Iteration 6
+'string'
+'string'
+string(8) "'string'"
+
+
+Iteration 7
+'NULL'
+'NULL'
+string(6) "'NULL'"
+
+
+Iteration 8
+'null'
+'null'
+string(6) "'null'"
+
+
+Iteration 9
+'FALSE'
+'FALSE'
+string(7) "'FALSE'"
+
+
+Iteration 10
+'false'
+'false'
+string(7) "'false'"
+
+
+Iteration 11
+'\v'
+'\v'
+string(3) "'\v'"
+
+
+Iteration 12
+'\000'
+'\000'
+string(6) "'\000'"
+
+
+Iteration 13
+'\\0'
+'\\0'
+string(5) "'\\0'"
+
+
+Iteration 14
+'\\060'
+'\\060'
+string(7) "'\\060'"
+
+
+Iteration 15
+'8'
+'8'
+string(3) "'8'"
+
+*** Testing var_export() with valid arrays ***
+
+*** Output for arrays ***
+
+Iteration 1
+array (
+)
+array (
+)
+string(9) "array (
+)"
+
+
+Iteration 2
+array (
+  0 => NULL,
+)
+array (
+  0 => NULL,
+)
+string(22) "array (
+  0 => NULL,
+)"
+
+
+Iteration 3
+array (
+  0 => NULL,
+)
+array (
+  0 => NULL,
+)
+string(22) "array (
+  0 => NULL,
+)"
+
+
+Iteration 4
+array (
+  0 => true,
+)
+array (
+  0 => true,
+)
+string(22) "array (
+  0 => true,
+)"
+
+
+Iteration 5
+array (
+  0 => '',
+)
+array (
+  0 => '',
+)
+string(20) "array (
+  0 => '',
+)"
+
+
+Iteration 6
+array (
+  0 => '',
+)
+array (
+  0 => '',
+)
+string(20) "array (
+  0 => '',
+)"
+
+
+Iteration 7
+array (
+  0 => 
+  array (
+  ),
+  1 => 
+  array (
+  ),
+)
+array (
+  0 => 
+  array (
+  ),
+  1 => 
+  array (
+  ),
+)
+string(55) "array (
+  0 => 
+  array (
+  ),
+  1 => 
+  array (
+  ),
+)"
+
+
+Iteration 8
+array (
+  0 => 
+  array (
+    0 => 1,
+    1 => 2,
+  ),
+  1 => 
+  array (
+    0 => 'a',
+    1 => 'b',
+  ),
+)
+array (
+  0 => 
+  array (
+    0 => 1,
+    1 => 2,
+  ),
+  1 => 
+  array (
+    0 => 'a',
+    1 => 'b',
+  ),
+)
+string(107) "array (
+  0 => 
+  array (
+    0 => 1,
+    1 => 2,
+  ),
+  1 => 
+  array (
+    0 => 'a',
+    1 => 'b',
+  ),
+)"
+
+
+Iteration 9
+array (
+  1 => 'One',
+)
+array (
+  1 => 'One',
+)
+string(23) "array (
+  1 => 'One',
+)"
+
+
+Iteration 10
+array (
+  'test' => 'is_array',
+)
+array (
+  'test' => 'is_array',
+)
+string(33) "array (
+  'test' => 'is_array',
+)"
+
+
+Iteration 11
+array (
+  0 => 0,
+)
+array (
+  0 => 0,
+)
+string(19) "array (
+  0 => 0,
+)"
+
+
+Iteration 12
+array (
+  0 => -1,
+)
+array (
+  0 => -1,
+)
+string(20) "array (
+  0 => -1,
+)"
+
+
+Iteration 13
+array (
+  0 => 10.5,
+  1 => 5.6,
+)
+array (
+  0 => 10.5,
+  1 => 5.6,
+)
+string(34) "array (
+  0 => 10.5,
+  1 => 5.6,
+)"
+
+
+Iteration 14
+array (
+  0 => 'string',
+  1 => 'test',
+)
+array (
+  0 => 'string',
+  1 => 'test',
+)
+string(41) "array (
+  0 => 'string',
+  1 => 'test',
+)"
+
+
+Iteration 15
+array (
+  0 => 'string',
+  1 => 'test',
+)
+array (
+  0 => 'string',
+  1 => 'test',
+)
+string(41) "array (
+  0 => 'string',
+  1 => 'test',
+)"
+
+*** Testing var_export() with valid objects ***
+
+*** Output for objects ***
+
+Iteration 1
+stdClass::__set_state(array(
+))
+stdClass::__set_state(array(
+))
+string(31) "stdClass::__set_state(array(
+))"
+
+
+Iteration 2
+foo::__set_state(array(
+))
+foo::__set_state(array(
+))
+string(26) "foo::__set_state(array(
+))"
+
+
+Iteration 3
+concreteClass::__set_state(array(
+))
+concreteClass::__set_state(array(
+))
+string(36) "concreteClass::__set_state(array(
+))"
+
+
+Iteration 4
+Value::__set_state(array(
+   'vars' => 
+  array (
+  ),
+))
+Value::__set_state(array(
+   'vars' => 
+  array (
+  ),
+))
+string(57) "Value::__set_state(array(
+   'vars' => 
+  array (
+  ),
+))"
+
+
+Iteration 5
+myClass::__set_state(array(
+   'foo_object' => 
+  foo::__set_state(array(
+  )),
+   'public_var' => 10,
+   'public_var1' => 
+  foo::__set_state(array(
+  )),
+   'private_var' => 
+  foo::__set_state(array(
+  )),
+   'protected_var' => NULL,
+   'proected_var' => 
+  foo::__set_state(array(
+  )),
+))
+myClass::__set_state(array(
+   'foo_object' => 
+  foo::__set_state(array(
+  )),
+   'public_var' => 10,
+   'public_var1' => 
+  foo::__set_state(array(
+  )),
+   'private_var' => 
+  foo::__set_state(array(
+  )),
+   'protected_var' => NULL,
+   'proected_var' => 
+  foo::__set_state(array(
+  )),
+))
+string(293) "myClass::__set_state(array(
+   'foo_object' => 
+  foo::__set_state(array(
+  )),
+   'public_var' => 10,
+   'public_var1' => 
+  foo::__set_state(array(
+  )),
+   'private_var' => 
+  foo::__set_state(array(
+  )),
+   'protected_var' => NULL,
+   'proected_var' => 
+  foo::__set_state(array(
+  )),
+))"
+
+
+Iteration 6
+myClass::__set_state(array(
+   'foo_object' => 
+  foo::__set_state(array(
+  )),
+   'public_var' => 10,
+   'public_var1' => 
+  foo::__set_state(array(
+  )),
+   'private_var' => 
+  foo::__set_state(array(
+  )),
+   'protected_var' => NULL,
+   'proected_var' => 
+  foo::__set_state(array(
+  )),
+))
+myClass::__set_state(array(
+   'foo_object' => 
+  foo::__set_state(array(
+  )),
+   'public_var' => 10,
+   'public_var1' => 
+  foo::__set_state(array(
+  )),
+   'private_var' => 
+  foo::__set_state(array(
+  )),
+   'protected_var' => NULL,
+   'proected_var' => 
+  foo::__set_state(array(
+  )),
+))
+string(293) "myClass::__set_state(array(
+   'foo_object' => 
+  foo::__set_state(array(
+  )),
+   'public_var' => 10,
+   'public_var1' => 
+  foo::__set_state(array(
+  )),
+   'private_var' => 
+  foo::__set_state(array(
+  )),
+   'protected_var' => NULL,
+   'proected_var' => 
+  foo::__set_state(array(
+  )),
+))"
+
+
+Iteration 7
+foo::__set_state(array(
+))
+foo::__set_state(array(
+))
+string(26) "foo::__set_state(array(
+))"
+
+
+Iteration 8
+foo::__set_state(array(
+))
+foo::__set_state(array(
+))
+string(26) "foo::__set_state(array(
+))"
+
+
+Iteration 9
+foo::__set_state(array(
+))
+foo::__set_state(array(
+))
+string(26) "foo::__set_state(array(
+))"
+
+
+Iteration 10
+Value::__set_state(array(
+   'vars' => 
+  array (
+  ),
+))
+Value::__set_state(array(
+   'vars' => 
+  array (
+  ),
+))
+string(57) "Value::__set_state(array(
+   'vars' => 
+  array (
+  ),
+))"
+
+
+Iteration 11
+concreteClass::__set_state(array(
+))
+concreteClass::__set_state(array(
+))
+string(36) "concreteClass::__set_state(array(
+))"
+
+*** Testing var_export() with valid null values ***
+
+*** Output for null values ***
+
+Iteration 1
+NULL
+NULL
+string(4) "NULL"
+
+
+Iteration 2
+NULL
+NULL
+string(4) "NULL"
+
+
+Iteration 3
+NULL
+NULL
+string(4) "NULL"
+
+
+*** Testing error conditions ***
+
+Warning: var_export() expects at least 1 parameter, 0 given in %s on line %d
+NULL
+Warning: var_export() expects at most 2 parameters, 3 given in %s on line %d
+NULL
+
+Done
index 0c7dcc8571b81053fdbbd659c6bb8cef390ea739..99ab7dad41d1069223b4df6243d932cc3a73c31d 100644 (file)
@@ -407,7 +407,7 @@ PHPAPI void php_var_export(zval **struc, int level TSRMLS_DC)
                php_printf("%ld", Z_LVAL_PP(struc));
                break;
        case IS_DOUBLE:
-               php_printf("%.*G", (int) EG(precision), Z_DVAL_PP(struc));
+               php_printf("%.*H", (int) EG(precision), Z_DVAL_PP(struc));
                break;
        case IS_STRING:
                tmp_str = php_addcslashes(Z_STRVAL_PP(struc), Z_STRLEN_PP(struc), &tmp_len, 0, "'\\\0", 3 TSRMLS_CC);
index 854e62b64ce419ce643656bab7c2a2ff08492225..d9aca450c3843da5a099038ce96aaa00f5b61988 100644 (file)
@@ -996,6 +996,7 @@ static int format_converter(register buffy * odp, const char *fmt, va_list ap) /
 
                                case 'g':
                                case 'G':
+                               case 'H':
                                        switch(modifier) {
                                                case LM_LONG_DOUBLE:
                                                        fp_num = (double) va_arg(ap, long double);
@@ -1035,7 +1036,7 @@ static int format_converter(register buffy * odp, const char *fmt, va_list ap) /
                                                lconv = localeconv();
                                        }
 #endif
-                                       s = php_gcvt(fp_num, precision, LCONV_DECIMAL_POINT, (*fmt == 'G')?'E':'e', &num_buf[1]);
+                                       s = php_gcvt(fp_num, precision, *fmt=='H' ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G')?'E':'e', &num_buf[1]);
                                        if (*s == '-') {
                                                prefix_char = *s++;
                                        } else if (print_sign) {
index ece05f3d7134bcb9c737fb9810b378b8900da920..8b576c0ed7f5a237211fd3342c1ada94f92c87d5 100644 (file)
@@ -566,7 +566,7 @@ static void xbuf_format_converter(smart_str *xbuf, const char *fmt, va_list ap)
 #endif
                                                s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
                                                 (adjust_precision == NO) ? FLOAT_DIGITS : precision,
-                                                (*fmt == 'f')?(*lconv->decimal_point):'.',
+                                                (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
                                                                        &is_negative, &num_buf[1], &s_len);
                                                if (is_negative)
                                                        prefix_char = '-';
@@ -580,6 +580,7 @@ static void xbuf_format_converter(smart_str *xbuf, const char *fmt, va_list ap)
 
                                case 'g':
                                case 'G':
+                               case 'H':
                                        switch(modifier) {
                                                case LM_LONG_DOUBLE:
                                                        fp_num = (double) va_arg(ap, long double);
@@ -618,7 +619,7 @@ static void xbuf_format_converter(smart_str *xbuf, const char *fmt, va_list ap)
                                                lconv = localeconv();
                                        }
 #endif
-                                       s = php_gcvt(fp_num, precision, *lconv->decimal_point, (*fmt == 'G')?'E':'e', &num_buf[1]);
+                                       s = php_gcvt(fp_num, precision, *fmt=='H' ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G')?'E':'e', &num_buf[1]);
                                        if (*s == '-')
                                                prefix_char = *s++;
                                        else if (print_sign)