writer: Simplify floats when printed

Add `tint::writer::FloatToString()`:

Converts the float `f` to a string using fixed-point notation (not scientific).
The float will be printed with the full precision required to describe the float.
All trailing `0`s will be omitted after the last non-zero fractional number,
unless the fractional is zero, in which case the number will end with `.0`.

Use this for the wgsl, msl and hlsl backends.

Change-Id: If5701136579e4398c31c673942f30e8877e9f813
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/33421
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 758fa7d..dd73a2f 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -424,6 +424,8 @@
     "src/validator/validator_impl.h",
     "src/validator/validator_test_helper.cc",
     "src/validator/validator_test_helper.h",
+    "src/writer/float_to_string.cc",
+    "src/writer/float_to_string.h",
     "src/writer/text.cc",
     "src/writer/text.h",
     "src/writer/text_generator.cc",
@@ -812,6 +814,7 @@
     "src/validator/validator_control_block_test.cc",
     "src/validator/validator_function_test.cc",
     "src/validator/validator_test.cc",
+    "src/writer/float_to_string_test.cc",
   ]
 
   configs += [
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 47390ee..fed1220 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -245,6 +245,8 @@
   validator/validator_impl.h
   validator/validator_test_helper.cc
   validator/validator_test_helper.h
+  writer/float_to_string.cc
+  writer/float_to_string.h
   writer/text.cc
   writer/text.h
   writer/text_generator.cc
@@ -423,6 +425,7 @@
   validator/validator_function_test.cc
   validator/validator_test.cc
   validator/validator_type_test.cc
+  writer/float_to_string_test.cc
 )
 
 ## Tint library
diff --git a/src/writer/float_to_string.cc b/src/writer/float_to_string.cc
new file mode 100644
index 0000000..b179ed0
--- /dev/null
+++ b/src/writer/float_to_string.cc
@@ -0,0 +1,37 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/writer/float_to_string.h"
+
+#include <limits>
+#include <sstream>
+
+namespace tint {
+namespace writer {
+
+std::string FloatToString(float f) {
+  std::stringstream ss;
+  ss.flags(ss.flags() | std::ios_base::showpoint | std::ios_base::fixed);
+  ss.precision(std::numeric_limits<float>::max_digits10);
+  ss << f;
+  auto str = ss.str();
+  while (str.length() >= 2 && str[str.size() - 1] == '0' &&
+         str[str.size() - 2] != '.') {
+    str.pop_back();
+  }
+  return str;
+}
+
+}  // namespace writer
+}  // namespace tint
diff --git a/src/writer/float_to_string.h b/src/writer/float_to_string.h
new file mode 100644
index 0000000..cd29134
--- /dev/null
+++ b/src/writer/float_to_string.h
@@ -0,0 +1,34 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_WRITER_FLOAT_TO_STRING_H_
+#define SRC_WRITER_FLOAT_TO_STRING_H_
+
+#include <string>
+
+namespace tint {
+namespace writer {
+
+/// Converts the float `f` to a string using fixed-point notation (not
+/// scientific). The float will be printed with the full precision required to
+/// describe the float. All trailing `0`s will be omitted after the last
+/// non-zero fractional number, unless the fractional is zero, in which case the
+/// number will end with `.0`.
+/// @return the float f formatted to a string
+std::string FloatToString(float f);
+
+}  // namespace writer
+}  // namespace tint
+
+#endif  // SRC_WRITER_FLOAT_TO_STRING_H_
diff --git a/src/writer/float_to_string_test.cc b/src/writer/float_to_string_test.cc
new file mode 100644
index 0000000..ca1a066
--- /dev/null
+++ b/src/writer/float_to_string_test.cc
@@ -0,0 +1,68 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/writer/float_to_string.h"
+
+#include <limits>
+
+#include "gtest/gtest.h"
+
+namespace tint {
+namespace writer {
+namespace {
+
+TEST(FloatToStringTest, Zero) {
+  EXPECT_EQ(FloatToString(0.0f), "0.0");
+}
+
+TEST(FloatToStringTest, One) {
+  EXPECT_EQ(FloatToString(1.0f), "1.0");
+}
+
+TEST(FloatToStringTest, MinusOne) {
+  EXPECT_EQ(FloatToString(-1.0f), "-1.0");
+}
+
+TEST(FloatToStringTest, Billion) {
+  EXPECT_EQ(FloatToString(1e9f), "1000000000.0");
+}
+
+TEST(FloatToStringTest, Small) {
+  EXPECT_NE(FloatToString(std::numeric_limits<float>::epsilon()), "0.0");
+}
+
+TEST(FloatToStringTest, Highest) {
+  if (std::numeric_limits<float>::max() !=
+      340282346638528859811704183484516925440.0f) {
+    GTEST_SKIP() << "std::numeric_limits<float>::max() is not as expected for "
+                    "this target";
+  }
+  EXPECT_EQ(FloatToString(std::numeric_limits<float>::max()),
+            "340282346638528859811704183484516925440.0");
+}
+
+TEST(FloatToStringTest, Lowest) {
+  if (std::numeric_limits<float>::lowest() !=
+      -340282346638528859811704183484516925440.0f) {
+    GTEST_SKIP()
+        << "std::numeric_limits<float>::lowest() is not as expected for "
+           "this target";
+  }
+  EXPECT_EQ(FloatToString(std::numeric_limits<float>::lowest()),
+            "-340282346638528859811704183484516925440.0");
+}
+
+}  // namespace
+}  // namespace writer
+}  // namespace tint
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index 91d1930..5d81f2c 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -51,6 +51,7 @@
 #include "src/ast/uint_literal.h"
 #include "src/ast/unary_op_expression.h"
 #include "src/ast/variable_decl_statement.h"
+#include "src/writer/float_to_string.h"
 
 namespace tint {
 namespace writer {
@@ -1455,16 +1456,7 @@
   if (lit->IsBool()) {
     out << (lit->AsBool()->IsTrue() ? "true" : "false");
   } else if (lit->IsFloat()) {
-    auto flags = out.flags();
-    auto precision = out.precision();
-
-    out.flags(flags | std::ios_base::showpoint);
-    out.precision(std::numeric_limits<float>::max_digits10);
-
-    out << lit->AsFloat()->value() << "f";
-
-    out.precision(precision);
-    out.flags(flags);
+    out << FloatToString(lit->AsFloat()->value()) << "f";
   } else if (lit->IsSint()) {
     out << lit->AsSint()->value();
   } else if (lit->IsUint()) {
diff --git a/src/writer/hlsl/generator_impl_binary_test.cc b/src/writer/hlsl/generator_impl_binary_test.cc
index baf428d..3f8d547 100644
--- a/src/writer/hlsl/generator_impl_binary_test.cc
+++ b/src/writer/hlsl/generator_impl_binary_test.cc
@@ -166,8 +166,8 @@
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
   EXPECT_EQ(result(),
-            "(float3(1.00000000f, 1.00000000f, 1.00000000f) * "
-            "1.00000000f)");
+            "(float3(1.0f, 1.0f, 1.0f) * "
+            "1.0f)");
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_ScalarVector) {
@@ -191,8 +191,8 @@
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
   EXPECT_EQ(result(),
-            "(1.00000000f * float3(1.00000000f, 1.00000000f, "
-            "1.00000000f))");
+            "(1.0f * float3(1.0f, 1.0f, "
+            "1.0f))");
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_MatrixScalar) {
@@ -210,7 +210,7 @@
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
-  EXPECT_EQ(result(), "(mat * 1.00000000f)");
+  EXPECT_EQ(result(), "(mat * 1.0f)");
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_ScalarMatrix) {
@@ -228,7 +228,7 @@
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
-  EXPECT_EQ(result(), "(1.00000000f * mat)");
+  EXPECT_EQ(result(), "(1.0f * mat)");
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_MatrixVector) {
@@ -254,8 +254,7 @@
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
-  EXPECT_EQ(result(),
-            "mul(mat, float3(1.00000000f, 1.00000000f, 1.00000000f))");
+  EXPECT_EQ(result(), "mul(mat, float3(1.0f, 1.0f, 1.0f))");
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_VectorMatrix) {
@@ -282,8 +281,7 @@
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
   EXPECT_TRUE(gen.EmitExpression(pre, out, &expr)) << gen.error();
-  EXPECT_EQ(result(),
-            "mul(float3(1.00000000f, 1.00000000f, 1.00000000f), mat)");
+  EXPECT_EQ(result(), "mul(float3(1.0f, 1.0f, 1.0f), mat)");
 }
 
 TEST_F(HlslGeneratorImplTest_Binary, Multiply_MatrixMatrix) {
diff --git a/src/writer/hlsl/generator_impl_constructor_test.cc b/src/writer/hlsl/generator_impl_constructor_test.cc
index 6590765..69c4768 100644
--- a/src/writer/hlsl/generator_impl_constructor_test.cc
+++ b/src/writer/hlsl/generator_impl_constructor_test.cc
@@ -70,7 +70,7 @@
   ast::ScalarConstructorExpression expr(lit);
 
   ASSERT_TRUE(gen.EmitConstructor(pre, out, &expr)) << gen.error();
-  EXPECT_EQ(result(), "1.07374182e+09f");
+  EXPECT_EQ(result(), "1073741824.0f");
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor, EmitConstructor_Type_Float) {
@@ -83,7 +83,7 @@
   ast::TypeConstructorExpression expr(&f32, values);
 
   ASSERT_TRUE(gen.EmitConstructor(pre, out, &expr)) << gen.error();
-  EXPECT_EQ(result(), "float(-1.20000004e-05f)");
+  EXPECT_EQ(result(), "float(-0.000012f)");
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor, EmitConstructor_Type_Bool) {
@@ -140,7 +140,7 @@
   ast::TypeConstructorExpression expr(&vec, values);
 
   ASSERT_TRUE(gen.EmitConstructor(pre, out, &expr)) << gen.error();
-  EXPECT_EQ(result(), "float3(1.00000000f, 2.00000000f, 3.00000000f)");
+  EXPECT_EQ(result(), "float3(1.0f, 2.0f, 3.0f)");
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor, EmitConstructor_Type_Vec_Empty) {
@@ -186,10 +186,8 @@
 
   // A matrix of type T with n columns and m rows can also be constructed from
   // n vectors of type T with m components.
-  EXPECT_EQ(
-      result(),
-      std::string("float3x2(float3(1.00000000f, 2.00000000f, 3.00000000f), ") +
-          "float3(3.00000000f, 4.00000000f, 5.00000000f))");
+  EXPECT_EQ(result(),
+            "float3x2(float3(1.0f, 2.0f, 3.0f), float3(3.0f, 4.0f, 5.0f))");
 }
 
 TEST_F(HlslGeneratorImplTest_Constructor, EmitConstructor_Type_Array) {
@@ -218,10 +216,9 @@
   ast::TypeConstructorExpression expr(&ary, ary_values);
 
   ASSERT_TRUE(gen.EmitConstructor(pre, out, &expr)) << gen.error();
-  EXPECT_EQ(result(), std::string("{") +
-                          "float3(1.00000000f, 2.00000000f, 3.00000000f), " +
-                          "float3(4.00000000f, 5.00000000f, 6.00000000f), " +
-                          "float3(7.00000000f, 8.00000000f, 9.00000000f)}");
+  EXPECT_EQ(result(),
+            "{float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f),"
+            " float3(7.0f, 8.0f, 9.0f)}");
 }
 
 // TODO(dsinclair): Add struct constructor test.
diff --git a/src/writer/hlsl/generator_impl_function_test.cc b/src/writer/hlsl/generator_impl_function_test.cc
index 9d07d84..fc81bd5 100644
--- a/src/writer/hlsl/generator_impl_function_test.cc
+++ b/src/writer/hlsl/generator_impl_function_test.cc
@@ -503,7 +503,7 @@
   EXPECT_EQ(result(), R"(RWByteAddressBuffer coord : register(u0);
 
 void frag_main() {
-  coord.Store(4, asuint(2.00000000f));
+  coord.Store(4, asuint(2.0f));
   return;
 }
 
@@ -588,7 +588,7 @@
 
 ep_1_out ep_1(ep_1_in tint_in) {
   ep_1_out tint_out;
-  tint_out.bar = sub_func_ep_1(tint_in, tint_out, 1.00000000f);
+  tint_out.bar = sub_func_ep_1(tint_in, tint_out, 1.0f);
   return tint_out;
 }
 
@@ -652,7 +652,7 @@
 
 ep_1_out ep_1() {
   ep_1_out tint_out;
-  tint_out.depth = sub_func(1.00000000f);
+  tint_out.depth = sub_func(1.0f);
   return tint_out;
 }
 
@@ -731,7 +731,7 @@
 
 ep_1_out ep_1(ep_1_in tint_in) {
   ep_1_out tint_out;
-  tint_out.depth = sub_func_ep_1(tint_in, tint_out, 1.00000000f);
+  tint_out.depth = sub_func_ep_1(tint_in, tint_out, 1.0f);
   return tint_out;
 }
 
@@ -797,7 +797,7 @@
 }
 
 void frag_main() {
-  float v = sub_func(1.00000000f);
+  float v = sub_func(1.0f);
   return;
 }
 
@@ -861,7 +861,7 @@
 }
 
 void frag_main() {
-  float v = sub_func(1.00000000f);
+  float v = sub_func(1.0f);
   return;
 }
 
@@ -916,7 +916,7 @@
 
 ep_1_out ep_1() {
   ep_1_out tint_out;
-  tint_out.bar = 1.00000000f;
+  tint_out.bar = 1.0f;
   if ((1 == 1)) {
     return tint_out;
   }
diff --git a/src/writer/hlsl/generator_impl_import_test.cc b/src/writer/hlsl/generator_impl_import_test.cc
index fff3fcd..a55c452 100644
--- a/src/writer/hlsl/generator_impl_import_test.cc
+++ b/src/writer/hlsl/generator_impl_import_test.cc
@@ -62,7 +62,7 @@
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
   ASSERT_TRUE(gen.EmitCall(pre, out, &expr)) << gen.error();
-  EXPECT_EQ(result(), std::string(param.hlsl_name) + "(1.00000000f)");
+  EXPECT_EQ(result(), std::string(param.hlsl_name) + "(1.0f)");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
                          HlslImportData_SingleParamTest,
@@ -130,8 +130,7 @@
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
   ASSERT_TRUE(gen.EmitCall(pre, out, &expr)) << gen.error();
-  EXPECT_EQ(result(),
-            std::string(param.hlsl_name) + "(1.00000000f, 2.00000000f)");
+  EXPECT_EQ(result(), std::string(param.hlsl_name) + "(1.0f, 2.0f)");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
                          HlslImportData_DualParamTest,
@@ -176,9 +175,9 @@
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
   ASSERT_TRUE(gen.EmitCall(pre, out, &expr)) << gen.error();
-  EXPECT_EQ(result(), std::string(param.hlsl_name) +
-                          "(float3(1.00000000f, 2.00000000f, 3.00000000f), "
-                          "float3(4.00000000f, 5.00000000f, 6.00000000f))");
+  EXPECT_EQ(result(),
+            std::string(param.hlsl_name) +
+                "(float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f))");
 }
 INSTANTIATE_TEST_SUITE_P(HlslGeneratorImplTest_Import,
                          HlslImportData_DualParam_VectorTest,
@@ -227,8 +226,7 @@
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
   ASSERT_TRUE(gen.EmitCall(pre, out, &expr)) << gen.error();
-  EXPECT_EQ(result(), std::string(param.hlsl_name) +
-                          "(1.00000000f, 2.00000000f, 3.00000000f)");
+  EXPECT_EQ(result(), std::string(param.hlsl_name) + "(1.0f, 2.0f, 3.0f)");
 }
 INSTANTIATE_TEST_SUITE_P(
     HlslGeneratorImplTest_Import,
diff --git a/src/writer/hlsl/generator_impl_loop_test.cc b/src/writer/hlsl/generator_impl_loop_test.cc
index 71bdd20..4e7bfdd 100644
--- a/src/writer/hlsl/generator_impl_loop_test.cc
+++ b/src/writer/hlsl/generator_impl_loop_test.cc
@@ -173,7 +173,7 @@
       }
       tint_hlsl_is_first_1 = false;
 
-      lhs = 2.40000010f;
+      lhs = 2.400000095f;
       other = 0.0f;
     }
   }
diff --git a/src/writer/hlsl/generator_impl_member_accessor_test.cc b/src/writer/hlsl/generator_impl_member_accessor_test.cc
index e7e1330..5c15ab6 100644
--- a/src/writer/hlsl/generator_impl_member_accessor_test.cc
+++ b/src/writer/hlsl/generator_impl_member_accessor_test.cc
@@ -619,7 +619,7 @@
 
   ASSERT_TRUE(td.DetermineResultType(&assign));
   ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
-  EXPECT_EQ(result(), R"(data.Store(4, asuint(2.00000000f));
+  EXPECT_EQ(result(), R"(data.Store(4, asuint(2.0f));
 )");
 }
 
@@ -826,9 +826,8 @@
 
   ASSERT_TRUE(td.DetermineResultType(&assign));
   ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
-  EXPECT_EQ(
-      result(),
-      R"(data.Store3(16, asuint(float3(1.00000000f, 2.00000000f, 3.00000000f)));
+  EXPECT_EQ(result(),
+            R"(data.Store3(16, asuint(float3(1.0f, 2.0f, 3.0f)));
 )");
 }
 
@@ -1194,9 +1193,8 @@
 
   ASSERT_TRUE(td.DetermineResultType(&assign));
   ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
-  EXPECT_EQ(
-      result(),
-      R"(data.Store3(16 + (32 * 2) + 0, asuint(float3(1.00000000f, 2.00000000f, 3.00000000f)));
+  EXPECT_EQ(result(),
+            R"(data.Store3(16 + (32 * 2) + 0, asuint(float3(1.0f, 2.0f, 3.0f)));
 )");
 }
 
@@ -1273,7 +1271,7 @@
   ASSERT_TRUE(td.DetermineResultType(&assign));
   ASSERT_TRUE(gen.EmitStatement(out, &assign)) << gen.error();
   EXPECT_EQ(result(),
-            R"(data.Store((4 * 1) + 16 + (32 * 2) + 0, asuint(1.00000000f));
+            R"(data.Store((4 * 1) + 16 + (32 * 2) + 0, asuint(1.0f));
 )");
 }
 
diff --git a/src/writer/hlsl/generator_impl_module_constant_test.cc b/src/writer/hlsl/generator_impl_module_constant_test.cc
index 3b78a97..84831d5 100644
--- a/src/writer/hlsl/generator_impl_module_constant_test.cc
+++ b/src/writer/hlsl/generator_impl_module_constant_test.cc
@@ -50,9 +50,7 @@
   var->set_constructor(create<ast::TypeConstructorExpression>(&ary, exprs));
 
   ASSERT_TRUE(gen.EmitProgramConstVariable(out, var)) << gen.error();
-  EXPECT_EQ(
-      result(),
-      "static const float pos[3] = {1.00000000f, 2.00000000f, 3.00000000f};\n");
+  EXPECT_EQ(result(), "static const float pos[3] = {1.0f, 2.0f, 3.0f};\n");
 }
 
 TEST_F(HlslGeneratorImplTest_ModuleConstant, Emit_SpecConstant) {
@@ -70,7 +68,7 @@
 
   ASSERT_TRUE(gen.EmitProgramConstVariable(out, var)) << gen.error();
   EXPECT_EQ(result(), R"(#ifndef WGSL_SPEC_CONSTANT_23
-#define WGSL_SPEC_CONSTANT_23 3.00000000f
+#define WGSL_SPEC_CONSTANT_23 3.0f
 #endif
 static const float pos = WGSL_SPEC_CONSTANT_23;
 #undef WGSL_SPEC_CONSTANT_23
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
index eecb08e..f0a1b8d 100644
--- a/src/writer/msl/generator_impl.cc
+++ b/src/writer/msl/generator_impl.cc
@@ -63,6 +63,7 @@
 #include "src/ast/uint_literal.h"
 #include "src/ast/unary_op_expression.h"
 #include "src/ast/variable_decl_statement.h"
+#include "src/writer/float_to_string.h"
 
 namespace tint {
 namespace writer {
@@ -863,16 +864,7 @@
   if (lit->IsBool()) {
     out_ << (lit->AsBool()->IsTrue() ? "true" : "false");
   } else if (lit->IsFloat()) {
-    auto flags = out_.flags();
-    auto precision = out_.precision();
-
-    out_.flags(flags | std::ios_base::showpoint);
-    out_.precision(std::numeric_limits<float>::max_digits10);
-
-    out_ << lit->AsFloat()->value() << "f";
-
-    out_.precision(precision);
-    out_.flags(flags);
+    out_ << FloatToString(lit->AsFloat()->value()) << "f";
   } else if (lit->IsSint()) {
     out_ << lit->AsSint()->value();
   } else if (lit->IsUint()) {
diff --git a/src/writer/msl/generator_impl_constructor_test.cc b/src/writer/msl/generator_impl_constructor_test.cc
index 27c8ec1..e1160b7 100644
--- a/src/writer/msl/generator_impl_constructor_test.cc
+++ b/src/writer/msl/generator_impl_constructor_test.cc
@@ -72,7 +72,7 @@
   ast::ScalarConstructorExpression expr(lit);
 
   ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
-  EXPECT_EQ(gen.result(), "1.07374182e+09f");
+  EXPECT_EQ(gen.result(), "1073741824.0f");
 }
 
 TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Float) {
@@ -85,7 +85,7 @@
   ast::TypeConstructorExpression expr(&f32, values);
 
   ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
-  EXPECT_EQ(gen.result(), "float(-1.20000004e-05f)");
+  EXPECT_EQ(gen.result(), "float(-0.000012f)");
 }
 
 TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Bool) {
@@ -142,7 +142,7 @@
   ast::TypeConstructorExpression expr(&vec, values);
 
   ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
-  EXPECT_EQ(gen.result(), "float3(1.00000000f, 2.00000000f, 3.00000000f)");
+  EXPECT_EQ(gen.result(), "float3(1.0f, 2.0f, 3.0f)");
 }
 
 TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Vec_Empty) {
@@ -187,10 +187,8 @@
 
   // A matrix of type T with n columns and m rows can also be constructed from
   // n vectors of type T with m components.
-  EXPECT_EQ(
-      gen.result(),
-      std::string("float2x3(float3(1.00000000f, 2.00000000f, 3.00000000f), ") +
-          "float3(3.00000000f, 4.00000000f, 5.00000000f))");
+  EXPECT_EQ(gen.result(),
+            "float2x3(float3(1.0f, 2.0f, 3.0f), float3(3.0f, 4.0f, 5.0f))");
 }
 
 TEST_F(MslGeneratorImplTest, EmitConstructor_Type_Array) {
@@ -219,10 +217,8 @@
   ast::TypeConstructorExpression expr(&ary, ary_values);
   ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
   EXPECT_EQ(gen.result(),
-            std::string("{") +
-                "float3(1.00000000f, 2.00000000f, 3.00000000f), " +
-                "float3(4.00000000f, 5.00000000f, 6.00000000f), " +
-                "float3(7.00000000f, 8.00000000f, 9.00000000f)}");
+            "{float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f), "
+            "float3(7.0f, 8.0f, 9.0f)}");
 }
 
 // TODO(dsinclair): Add struct constructor test.
diff --git a/src/writer/msl/generator_impl_function_test.cc b/src/writer/msl/generator_impl_function_test.cc
index 5828ee2..a2b6969 100644
--- a/src/writer/msl/generator_impl_function_test.cc
+++ b/src/writer/msl/generator_impl_function_test.cc
@@ -499,7 +499,7 @@
 
 fragment ep_1_out ep_1(ep_1_in tint_in [[stage_in]]) {
   ep_1_out tint_out = {};
-  tint_out.bar = sub_func_ep_1(tint_in, tint_out, 1.00000000f);
+  tint_out.bar = sub_func_ep_1(tint_in, tint_out, 1.0f);
   return tint_out;
 }
 
@@ -567,7 +567,7 @@
 
 fragment ep_1_out ep_1() {
   ep_1_out tint_out = {};
-  tint_out.depth = sub_func(1.00000000f);
+  tint_out.depth = sub_func(1.0f);
   return tint_out;
 }
 
@@ -644,7 +644,7 @@
 
 fragment ep_1_out ep_1(float4 coord [[position]]) {
   ep_1_out tint_out = {};
-  tint_out.depth = sub_func_ep_1(tint_out, coord, 1.00000000f);
+  tint_out.depth = sub_func_ep_1(tint_out, coord, 1.0f);
   return tint_out;
 }
 
@@ -709,7 +709,7 @@
 }
 
 fragment void frag_main(constant float4& coord [[buffer(0)]]) {
-  float v = sub_func(coord, 1.00000000f);
+  float v = sub_func(coord, 1.0f);
   return;
 }
 
@@ -796,7 +796,7 @@
 }
 
 fragment void frag_main(device Data& coord [[buffer(0)]]) {
-  float v = sub_func(coord, 1.00000000f);
+  float v = sub_func(coord, 1.0f);
   return;
 }
 
@@ -883,7 +883,7 @@
 }
 
 fragment void frag_main(const device Data& coord [[buffer(0)]]) {
-  float v = sub_func(coord, 1.00000000f);
+  float v = sub_func(coord, 1.0f);
   return;
 }
 
@@ -941,7 +941,7 @@
 
 fragment ep_1_out ep_1() {
   ep_1_out tint_out = {};
-  tint_out.bar = 1.00000000f;
+  tint_out.bar = 1.0f;
   if ((1 == 1)) {
     return tint_out;
   }
diff --git a/src/writer/msl/generator_impl_import_test.cc b/src/writer/msl/generator_impl_import_test.cc
index 9d3b71a..ef5428d 100644
--- a/src/writer/msl/generator_impl_import_test.cc
+++ b/src/writer/msl/generator_impl_import_test.cc
@@ -126,8 +126,8 @@
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
   ASSERT_TRUE(gen.EmitCall(&expr)) << gen.error();
-  EXPECT_EQ(gen.result(), std::string("metal::") + param.msl_name +
-                              "(1.00000000f, 2.00000000f)");
+  EXPECT_EQ(gen.result(),
+            std::string("metal::") + param.msl_name + "(1.0f, 2.0f)");
 }
 INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest,
                          MslImportData_DualParamTest,
@@ -175,8 +175,8 @@
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
   ASSERT_TRUE(gen.EmitCall(&expr)) << gen.error();
   EXPECT_EQ(gen.result(), std::string("metal::") + param.msl_name +
-                              "(float3(1.00000000f, 2.00000000f, 3.00000000f), "
-                              "float3(4.00000000f, 5.00000000f, 6.00000000f))");
+                              "(float3(1.0f, 2.0f, 3.0f), "
+                              "float3(4.0f, 5.0f, 6.0f))");
 }
 INSTANTIATE_TEST_SUITE_P(MslGeneratorImplTest,
                          MslImportData_DualParam_VectorTest,
@@ -225,8 +225,8 @@
 
   ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
   ASSERT_TRUE(gen.EmitCall(&expr)) << gen.error();
-  EXPECT_EQ(gen.result(), std::string("metal::") + param.msl_name +
-                              "(1.00000000f, 2.00000000f, 3.00000000f)");
+  EXPECT_EQ(gen.result(),
+            std::string("metal::") + param.msl_name + "(1.0f, 2.0f, 3.0f)");
 }
 INSTANTIATE_TEST_SUITE_P(
     MslGeneratorImplTest,
diff --git a/src/writer/msl/generator_impl_loop_test.cc b/src/writer/msl/generator_impl_loop_test.cc
index 800b51b..46741e0 100644
--- a/src/writer/msl/generator_impl_loop_test.cc
+++ b/src/writer/msl/generator_impl_loop_test.cc
@@ -179,7 +179,7 @@
       }
       tint_msl_is_first_1 = false;
 
-      lhs = 2.40000010f;
+      lhs = 2.400000095f;
       other = 0.0f;
     }
   }
diff --git a/src/writer/msl/generator_impl_module_constant_test.cc b/src/writer/msl/generator_impl_module_constant_test.cc
index 1612537..6c7b997 100644
--- a/src/writer/msl/generator_impl_module_constant_test.cc
+++ b/src/writer/msl/generator_impl_module_constant_test.cc
@@ -52,9 +52,7 @@
   var->set_constructor(create<ast::TypeConstructorExpression>(&ary, exprs));
 
   ASSERT_TRUE(gen.EmitProgramConstVariable(var)) << gen.error();
-  EXPECT_EQ(
-      gen.result(),
-      "constant float pos[3] = {1.00000000f, 2.00000000f, 3.00000000f};\n");
+  EXPECT_EQ(gen.result(), "constant float pos[3] = {1.0f, 2.0f, 3.0f};\n");
 }
 
 TEST_F(MslGeneratorImplTest, Emit_SpecConstant) {
diff --git a/src/writer/wgsl/generator_impl.cc b/src/writer/wgsl/generator_impl.cc
index bd345bd..7c49dd2 100644
--- a/src/writer/wgsl/generator_impl.cc
+++ b/src/writer/wgsl/generator_impl.cc
@@ -67,6 +67,7 @@
 #include "src/ast/unary_op_expression.h"
 #include "src/ast/variable_decl_statement.h"
 #include "src/ast/workgroup_decoration.h"
+#include "src/writer/float_to_string.h"
 
 namespace tint {
 namespace writer {
@@ -318,16 +319,7 @@
   if (lit->IsBool()) {
     out_ << (lit->AsBool()->IsTrue() ? "true" : "false");
   } else if (lit->IsFloat()) {
-    auto flags = out_.flags();
-    auto precision = out_.precision();
-
-    out_.flags(flags | std::ios_base::showpoint);
-    out_.precision(std::numeric_limits<float>::max_digits10);
-
-    out_ << lit->AsFloat()->value();
-
-    out_.precision(precision);
-    out_.flags(flags);
+    out_ << FloatToString(lit->AsFloat()->value());
   } else if (lit->IsSint()) {
     out_ << lit->AsSint()->value();
   } else if (lit->IsUint()) {
diff --git a/src/writer/wgsl/generator_impl_constructor_test.cc b/src/writer/wgsl/generator_impl_constructor_test.cc
index 53ac451..850f5ec 100644
--- a/src/writer/wgsl/generator_impl_constructor_test.cc
+++ b/src/writer/wgsl/generator_impl_constructor_test.cc
@@ -70,7 +70,7 @@
   ast::ScalarConstructorExpression expr(lit);
 
   ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
-  EXPECT_EQ(gen.result(), "1.07374182e+09");
+  EXPECT_EQ(gen.result(), "1073741824.0");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Float) {
@@ -83,7 +83,7 @@
   ast::TypeConstructorExpression expr(&f32, values);
 
   ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
-  EXPECT_EQ(gen.result(), "f32(-1.20000004e-05)");
+  EXPECT_EQ(gen.result(), "f32(-0.000012)");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Bool) {
@@ -140,7 +140,7 @@
   ast::TypeConstructorExpression expr(&vec, values);
 
   ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
-  EXPECT_EQ(gen.result(), "vec3<f32>(1.00000000, 2.00000000, 3.00000000)");
+  EXPECT_EQ(gen.result(), "vec3<f32>(1.0, 2.0, 3.0)");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Mat) {
@@ -167,10 +167,8 @@
   ast::TypeConstructorExpression expr(&mat, mat_values);
 
   ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
-  EXPECT_EQ(gen.result(),
-            std::string("mat2x3<f32>(vec2<f32>(1.00000000, 2.00000000), ") +
-                "vec2<f32>(3.00000000, 4.00000000), " +
-                "vec2<f32>(5.00000000, 6.00000000))");
+  EXPECT_EQ(gen.result(), std::string("mat2x3<f32>(vec2<f32>(1.0, 2.0), ") +
+                              "vec2<f32>(3.0, 4.0), " + "vec2<f32>(5.0, 6.0))");
 }
 
 TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Array) {
@@ -200,10 +198,8 @@
 
   ASSERT_TRUE(gen.EmitConstructor(&expr)) << gen.error();
   EXPECT_EQ(gen.result(),
-            std::string("array<vec3<f32>, 3>(") +
-                "vec3<f32>(1.00000000, 2.00000000, 3.00000000), " +
-                "vec3<f32>(4.00000000, 5.00000000, 6.00000000), " +
-                "vec3<f32>(7.00000000, 8.00000000, 9.00000000))");
+            std::string("array<vec3<f32>, 3>(") + "vec3<f32>(1.0, 2.0, 3.0), " +
+                "vec3<f32>(4.0, 5.0, 6.0), " + "vec3<f32>(7.0, 8.0, 9.0))");
 }
 
 }  // namespace