Jelajahi Sumber

Format: Strip trailing zeroes from floating point values

This is a pretty naive implementation that works well. The precision
parameter is interpreted as "maximum precision" instead of "minimum
precision", which in my opinion is the most useful interpretation.
Jelle Raaijmakers 4 tahun lalu
induk
melakukan
c4e19250a1
2 mengubah file dengan 20 tambahan dan 11 penghapusan
  1. 14 5
      AK/Format.cpp
  2. 6 6
      AK/Tests/TestFormat.cpp

+ 14 - 5
AK/Format.cpp

@@ -391,7 +391,6 @@ void FormatBuilder::put_f64(
     FormatBuilder format_builder { string_builder };
 
     format_builder.put_i64(static_cast<i64>(value), base, false, upper_case, false, Align::Right, 0, ' ', sign_mode);
-    string_builder.append('.');
 
     if (precision > 0) {
         // FIXME: This is a terrible approximation but doing it properly would be a lot of work. If someone is up for that, a good
@@ -402,12 +401,22 @@ void FormatBuilder::put_f64(
         if (value < 0)
             value = -value;
 
-        for (u32 i = 0; i < precision; ++i)
-            value *= 10;
+        double epsilon = 0.5;
+        for (size_t i = 0; i < precision; ++i)
+            epsilon /= 10.0;
 
-        format_builder.put_u64(static_cast<u64>(value), base, false, upper_case, true, Align::Right, precision);
+        size_t visible_precision = 0;
+        for (; visible_precision < precision; ++visible_precision) {
+            if (value - static_cast<i64>(value) < epsilon)
+                break;
+            value *= 10.0;
+            epsilon *= 10.0;
+        }
 
-        // FIXME: Cut off trailing zeroes by default?
+        if (visible_precision > 0) {
+            string_builder.append('.');
+            format_builder.put_u64(static_cast<u64>(value), base, false, upper_case, true, Align::Right, visible_precision);
+        }
     }
 
     put_string(string_builder.string_view(), align, min_width, NumericLimits<size_t>::max(), fill);

+ 6 - 6
AK/Tests/TestFormat.cpp

@@ -252,11 +252,11 @@ TEST_CASE(file_descriptor)
 
 TEST_CASE(floating_point_numbers)
 {
-    EXPECT_EQ(String::formatted("{}", 1.12), "1.120000");
-    EXPECT_EQ(String::formatted("{}", 1.), "1.000000");
-    EXPECT_EQ(String::formatted("{:.3}", 1.12), "1.120");
+    EXPECT_EQ(String::formatted("{}", 1.12), "1.12");
+    EXPECT_EQ(String::formatted("{}", 1.), "1");
+    EXPECT_EQ(String::formatted("{:.3}", 1.12), "1.12");
     EXPECT_EQ(String::formatted("{:.1}", 1.12), "1.1");
-    EXPECT_EQ(String::formatted("{}", -1.12), "-1.120000");
+    EXPECT_EQ(String::formatted("{}", -1.12), "-1.12");
 
     // FIXME: There is always the question what we mean with the width field. Do we mean significant digits?
     //        Do we mean the whole width? This is what was the simplest to implement:
@@ -265,12 +265,12 @@ TEST_CASE(floating_point_numbers)
 
 TEST_CASE(no_precision_no_trailing_number)
 {
-    EXPECT_EQ(String::formatted("{:.0}", 0.1), "0.");
+    EXPECT_EQ(String::formatted("{:.0}", 0.1), "0");
 }
 
 TEST_CASE(yay_this_implementation_sucks)
 {
-    EXPECT_EQ(String::formatted("{:.0}", .99999999999), "0.");
+    EXPECT_EQ(String::formatted("{:.0}", .99999999999), "0");
 }
 
 TEST_CASE(format_nullptr)