Merge pull request #232 from nlohmann/issue228

make serialization locale-independent (fixes #228)
diff --git a/src/json.hpp b/src/json.hpp
index 0594b38..8f671fb 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -88,6 +88,19 @@
     static constexpr bool value = sizeof(test<T>(0)) == 1;
 };
 
+/*!
+@brief helper class to create locales with decimal point
+@sa https://github.com/nlohmann/json/issues/51#issuecomment-86869315
+*/
+class DecimalSeparator : public std::numpunct<char>
+{
+  protected:
+    char do_decimal_point() const
+    {
+        return '.';
+    }
+};
+
 }
 
 /*!
@@ -6114,24 +6127,26 @@
 
             case value_t::number_float:
             {
-                // buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1)
-                char buf[263];
-                int len;
-
                 // check if number was parsed from a string
                 if (m_type.bits.parsed)
                 {
                     // check if parsed number had an exponent given
                     if (m_type.bits.has_exp)
                     {
+                        // buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1)
+                        char buf[263];
+                        int len;
+
                         // handle capitalization of the exponent
                         if (m_type.bits.exp_cap)
                         {
-                            len = snprintf(buf, sizeof(buf), "%.*E", m_type.bits.precision, m_value.number_float) + 1;
+                            len = snprintf(buf, sizeof(buf), "%.*E",
+                                           m_type.bits.precision, m_value.number_float) + 1;
                         }
                         else
                         {
-                            len = snprintf(buf, sizeof(buf), "%.*e", m_type.bits.precision, m_value.number_float) + 1;
+                            len = snprintf(buf, sizeof(buf), "%.*e",
+                                           m_type.bits.precision, m_value.number_float) + 1;
                         }
 
                         // remove '+' sign from the exponent if necessary
@@ -6152,40 +6167,40 @@
                                 }
                             }
                         }
+
+                        o << buf;
                     }
                     else
                     {
                         // no exponent - output as a decimal
-                        snprintf(buf, sizeof(buf), "%.*f",
-                                 m_type.bits.precision, m_value.number_float);
+                        std::stringstream ss;
+                        ss.imbue(std::locale(std::locale(), new DecimalSeparator));  // fix locale problems
+                        ss << std::setprecision(m_type.bits.precision)
+                           << std::fixed << m_value.number_float;
+                        o << ss.str();
                     }
                 }
-                else if (m_value.number_float == 0)
-                {
-                    // special case for zero to get "0.0"/"-0.0"
-                    if (std::signbit(m_value.number_float))
-                    {
-                        o << "-0.0";
-                    }
-                    else
-                    {
-                        o << "0.0";
-                    }
-                    return;
-                }
                 else
                 {
-                    // Otherwise 6, 15 or 16 digits of precision allows
-                    // round-trip IEEE 754 string->float->string,
-                    // string->double->string or string->long double->string;
-                    // to be safe, we read this value from
-                    // std::numeric_limits<number_float_t>::digits10
-                    snprintf(buf, sizeof(buf), "%.*g",
-                             std::numeric_limits<double>::digits10,
-                             m_value.number_float);
+                    if (m_value.number_float == 0)
+                    {
+                        // special case for zero to get "0.0"/"-0.0"
+                        o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
+                    }
+                    else
+                    {
+                        // Otherwise 6, 15 or 16 digits of precision allows
+                        // round-trip IEEE 754 string->float->string,
+                        // string->double->string or string->long double->string;
+                        // to be safe, we read this value from
+                        // std::numeric_limits<number_float_t>::digits10
+                        std::stringstream ss;
+                        ss.imbue(std::locale(std::locale(), new DecimalSeparator));  // fix locale problems
+                        ss << std::setprecision(std::numeric_limits<double>::digits10)
+                           << m_value.number_float;
+                        o << ss.str();
+                    }
                 }
-
-                o << buf;
                 return;
             }
 
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index f4ddacf..ebf83d8 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -88,6 +88,19 @@
     static constexpr bool value = sizeof(test<T>(0)) == 1;
 };
 
+/*!
+@brief helper class to create locales with decimal point
+@sa https://github.com/nlohmann/json/issues/51#issuecomment-86869315
+*/
+class DecimalSeparator : public std::numpunct<char>
+{
+  protected:
+    char do_decimal_point() const
+    {
+        return '.';
+    }
+};
+
 }
 
 /*!
@@ -6114,24 +6127,26 @@
 
             case value_t::number_float:
             {
-                // buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1)
-                char buf[263];
-                int len;
-
                 // check if number was parsed from a string
                 if (m_type.bits.parsed)
                 {
                     // check if parsed number had an exponent given
                     if (m_type.bits.has_exp)
                     {
+                        // buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1)
+                        char buf[263];
+                        int len;
+
                         // handle capitalization of the exponent
                         if (m_type.bits.exp_cap)
                         {
-                            len = snprintf(buf, sizeof(buf), "%.*E", m_type.bits.precision, m_value.number_float) + 1;
+                            len = snprintf(buf, sizeof(buf), "%.*E",
+                                           m_type.bits.precision, m_value.number_float) + 1;
                         }
                         else
                         {
-                            len = snprintf(buf, sizeof(buf), "%.*e", m_type.bits.precision, m_value.number_float) + 1;
+                            len = snprintf(buf, sizeof(buf), "%.*e",
+                                           m_type.bits.precision, m_value.number_float) + 1;
                         }
 
                         // remove '+' sign from the exponent if necessary
@@ -6152,40 +6167,40 @@
                                 }
                             }
                         }
+
+                        o << buf;
                     }
                     else
                     {
                         // no exponent - output as a decimal
-                        snprintf(buf, sizeof(buf), "%.*f",
-                                 m_type.bits.precision, m_value.number_float);
+                        std::stringstream ss;
+                        ss.imbue(std::locale(std::locale(), new DecimalSeparator));  // fix locale problems
+                        ss << std::setprecision(m_type.bits.precision)
+                           << std::fixed << m_value.number_float;
+                        o << ss.str();
                     }
                 }
-                else if (m_value.number_float == 0)
-                {
-                    // special case for zero to get "0.0"/"-0.0"
-                    if (std::signbit(m_value.number_float))
-                    {
-                        o << "-0.0";
-                    }
-                    else
-                    {
-                        o << "0.0";
-                    }
-                    return;
-                }
                 else
                 {
-                    // Otherwise 6, 15 or 16 digits of precision allows
-                    // round-trip IEEE 754 string->float->string,
-                    // string->double->string or string->long double->string;
-                    // to be safe, we read this value from
-                    // std::numeric_limits<number_float_t>::digits10
-                    snprintf(buf, sizeof(buf), "%.*g",
-                             std::numeric_limits<double>::digits10,
-                             m_value.number_float);
+                    if (m_value.number_float == 0)
+                    {
+                        // special case for zero to get "0.0"/"-0.0"
+                        o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
+                    }
+                    else
+                    {
+                        // Otherwise 6, 15 or 16 digits of precision allows
+                        // round-trip IEEE 754 string->float->string,
+                        // string->double->string or string->long double->string;
+                        // to be safe, we read this value from
+                        // std::numeric_limits<number_float_t>::digits10
+                        std::stringstream ss;
+                        ss.imbue(std::locale(std::locale(), new DecimalSeparator));  // fix locale problems
+                        ss << std::setprecision(std::numeric_limits<double>::digits10)
+                           << m_value.number_float;
+                        o << ss.str();
+                    }
                 }
-
-                o << buf;
                 return;
             }
 
diff --git a/test/unit.cpp b/test/unit.cpp
index b440a28..ab96364 100644
--- a/test/unit.cpp
+++ b/test/unit.cpp
@@ -12369,5 +12369,43 @@
         j_long_double = 1.23e45L;
         CHECK(j_long_double.get<long double>() == 1.23e45L);
     }
+
+    SECTION("issue #228 - double values are serialized with commas as decimal points")
+    {
+        json j1a = 23.42;
+        json j1b = json::parse("23.42");
+
+        json j2a = 2342e-2;
+        //issue #230
+        //json j2b = json::parse("2342e-2");
+
+        json j3a = 10E3;
+        json j3b = json::parse("10E3");
+        json j3c = json::parse("10e3");
+
+        // class to create a locale that would use a comma for decimals
+        class CommaDecimalSeparator : public std::numpunct<char>
+        {
+          protected:
+            char do_decimal_point() const
+            {
+                return ',';
+            }
+        };
+
+        // change locale to mess with decimal points
+        std::locale::global(std::locale(std::locale(), new CommaDecimalSeparator));
+
+        CHECK(j1a.dump() == "23.42");
+        CHECK(j1b.dump() == "23.42");
+
+        CHECK(j2a.dump() == "23.42");
+        //issue #230
+        //CHECK(j2b.dump() == "23.42");
+
+        CHECK(j3a.dump() == "10000");
+        CHECK(j3b.dump() == "1E04");
+        CHECK(j3c.dump() == "1e04");
+    }
 }