Skip to content

Commit c863c2f

Browse files
NickGerlemanfacebook-github-bot
authored andcommitted
Round debug float output to 4 decimal places (#55879)
Summary: Float values in debug layout metrics output (used by Fantom test assertions) are subject to floating-point precision differences across platforms and compilers (e.g., `66.6667` vs `66.66666412353516`), causing flaky test comparisons. This changes `toString(const double&)` in `DebugStringConvertible.cpp` to format floats with 4 decimal places using `snprintf("%.4f")` and strip trailing zeros, replacing `double-conversion`'s `ToShortest()`. This is debug-only code (guarded by `#if RN_DEBUG_STRING_CONVERTIBLE`) so the precision change is safe for all consumers. The `double-conversion` dependency is also removed from the BUCK file since it is no longer used. Output examples: - `0.0` → `"0"` - `100.0` → `"100"` - `66.66666412353516` → `"66.6667"` - `1.5` → `"1.5"` - `33.333333` → `"33.3333"` Changelog: [Internal] Reviewed By: javache Differential Revision: D95041966
1 parent 40c9ed1 commit c863c2f

File tree

2 files changed

+46
-54
lines changed

2 files changed

+46
-54
lines changed

packages/react-native/ReactCommon/react/renderer/components/view/conversions.h

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <react/renderer/css/CSSTransform.h>
2323
#include <react/renderer/css/CSSTransformOrigin.h>
2424
#include <react/renderer/css/CSSValueParser.h>
25+
#include <react/renderer/debug/DebugStringConvertible.h>
2526
#include <react/renderer/debug/flags.h>
2627
#include <react/renderer/graphics/BackgroundPosition.h>
2728
#include <react/renderer/graphics/BackgroundRepeat.h>
@@ -1592,9 +1593,9 @@ inline std::string toString(const std::array<float, N> vec)
15921593

15931594
s.append("{");
15941595
for (size_t i = 0; i < N - 1; i++) {
1595-
s.append(std::to_string(vec[i]) + ", ");
1596+
s.append(toString(vec[i]) + ", ");
15961597
}
1597-
s.append(std::to_string(vec[N - 1]));
1598+
s.append(toString(vec[N - 1]));
15981599
s.append("}");
15991600

16001601
return s;
@@ -1647,9 +1648,9 @@ inline std::string toString(const yoga::Style::Length &length)
16471648
} else if (length.isAuto()) {
16481649
return "auto";
16491650
} else if (length.isPoints()) {
1650-
return std::to_string(length.value().unwrap());
1651+
return toString(length.value().unwrap());
16511652
} else if (length.isPercent()) {
1652-
return std::to_string(length.value().unwrap()) + "%";
1653+
return toString(length.value().unwrap()) + "%";
16531654
} else {
16541655
return "unknown";
16551656
}
@@ -1662,9 +1663,9 @@ inline std::string toString(const yoga::Style::SizeLength &length)
16621663
} else if (length.isAuto()) {
16631664
return "auto";
16641665
} else if (length.isPoints()) {
1665-
return std::to_string(length.value().unwrap());
1666+
return toString(length.value().unwrap());
16661667
} else if (length.isPercent()) {
1667-
return std::to_string(length.value().unwrap()) + "%";
1668+
return toString(length.value().unwrap()) + "%";
16681669
} else if (length.isMaxContent()) {
16691670
return "max-content";
16701671
} else if (length.isFitContent()) {
@@ -1682,7 +1683,7 @@ inline std::string toString(const yoga::FloatOptional &value)
16821683
return "undefined";
16831684
}
16841685

1685-
return std::to_string(value.unwrap());
1686+
return toString(value.unwrap());
16861687
}
16871688

16881689
inline std::string toString(const LayoutConformance &value)
@@ -1698,14 +1699,10 @@ inline std::string toString(const LayoutConformance &value)
16981699
inline std::string toString(const std::array<Float, 16> &m)
16991700
{
17001701
std::string result;
1701-
result += "[ " + std::to_string(m[0]) + " " + std::to_string(m[1]) + " " + std::to_string(m[2]) + " " +
1702-
std::to_string(m[3]) + " ]\n";
1703-
result += "[ " + std::to_string(m[4]) + " " + std::to_string(m[5]) + " " + std::to_string(m[6]) + " " +
1704-
std::to_string(m[7]) + " ]\n";
1705-
result += "[ " + std::to_string(m[8]) + " " + std::to_string(m[9]) + " " + std::to_string(m[10]) + " " +
1706-
std::to_string(m[11]) + " ]\n";
1707-
result += "[ " + std::to_string(m[12]) + " " + std::to_string(m[13]) + " " + std::to_string(m[14]) + " " +
1708-
std::to_string(m[15]) + " ]";
1702+
result += "[ " + toString(m[0]) + " " + toString(m[1]) + " " + toString(m[2]) + " " + toString(m[3]) + " ]\n";
1703+
result += "[ " + toString(m[4]) + " " + toString(m[5]) + " " + toString(m[6]) + " " + toString(m[7]) + " ]\n";
1704+
result += "[ " + toString(m[8]) + " " + toString(m[9]) + " " + toString(m[10]) + " " + toString(m[11]) + " ]\n";
1705+
result += "[ " + toString(m[12]) + " " + toString(m[13]) + " " + toString(m[14]) + " " + toString(m[15]) + " ]";
17091706
return result;
17101707
}
17111708

@@ -1722,48 +1719,48 @@ inline std::string toString(const Transform &transform)
17221719

17231720
switch (operation.type) {
17241721
case TransformOperationType::Perspective: {
1725-
result += "{\"perspective\": " + std::to_string(operation.x.value) + "}";
1722+
result += "{\"perspective\": " + toString(operation.x.value) + "}";
17261723
break;
17271724
}
17281725
case TransformOperationType::Rotate: {
17291726
if (operation.x.value != 0 && operation.y.value == 0 && operation.z.value == 0) {
1730-
result += R"({"rotateX": ")" + std::to_string(operation.x.value) + "rad\"}";
1727+
result += R"({"rotateX": ")" + toString(operation.x.value) + "rad\"}";
17311728
} else if (operation.x.value == 0 && operation.y.value != 0 && operation.z.value == 0) {
1732-
result += R"({"rotateY": ")" + std::to_string(operation.y.value) + "rad\"}";
1729+
result += R"({"rotateY": ")" + toString(operation.y.value) + "rad\"}";
17331730
} else if (operation.x.value == 0 && operation.y.value == 0 && operation.z.value != 0) {
1734-
result += R"({"rotateZ": ")" + std::to_string(operation.z.value) + "rad\"}";
1731+
result += R"({"rotateZ": ")" + toString(operation.z.value) + "rad\"}";
17351732
}
17361733
break;
17371734
}
17381735
case TransformOperationType::Scale: {
17391736
if (operation.x.value == operation.y.value && operation.x.value == operation.z.value) {
1740-
result += "{\"scale\": " + std::to_string(operation.x.value) + "}";
1737+
result += "{\"scale\": " + toString(operation.x.value) + "}";
17411738
} else if (operation.y.value == 1 && operation.z.value == 1) {
1742-
result += "{\"scaleX\": " + std::to_string(operation.x.value) + "}";
1739+
result += "{\"scaleX\": " + toString(operation.x.value) + "}";
17431740
} else if (operation.x.value == 1 && operation.z.value == 1) {
1744-
result += "{\"scaleY\": " + std::to_string(operation.y.value) + "}";
1741+
result += "{\"scaleY\": " + toString(operation.y.value) + "}";
17451742
} else if (operation.x.value == 1 && operation.y.value == 1) {
1746-
result += "{\"scaleZ\": " + std::to_string(operation.z.value) + "}";
1743+
result += "{\"scaleZ\": " + toString(operation.z.value) + "}";
17471744
}
17481745
break;
17491746
}
17501747
case TransformOperationType::Translate: {
17511748
if (operation.x.value != 0 && operation.y.value != 0 && operation.z.value == 0) {
17521749
result += "{\"translate\": [";
1753-
result += std::to_string(operation.x.value) + ", " + std::to_string(operation.y.value);
1750+
result += toString(operation.x.value) + ", " + toString(operation.y.value);
17541751
result += "]}";
17551752
} else if (operation.x.value != 0 && operation.y.value == 0) {
1756-
result += "{\"translateX\": " + std::to_string(operation.x.value) + "}";
1753+
result += "{\"translateX\": " + toString(operation.x.value) + "}";
17571754
} else if (operation.x.value == 0 && operation.y.value != 0) {
1758-
result += "{\"translateY\": " + std::to_string(operation.y.value) + "}";
1755+
result += "{\"translateY\": " + toString(operation.y.value) + "}";
17591756
}
17601757
break;
17611758
}
17621759
case TransformOperationType::Skew: {
17631760
if (operation.x.value != 0 && operation.y.value == 0) {
1764-
result += R"({"skewX": ")" + std::to_string(operation.x.value) + "rad\"}";
1761+
result += R"({"skewX": ")" + toString(operation.x.value) + "rad\"}";
17651762
} else if (operation.x.value == 0 && operation.y.value != 0) {
1766-
result += R"({"skewY": ")" + std::to_string(operation.y.value) + "rad\"}";
1763+
result += R"({"skewY": ")" + toString(operation.y.value) + "rad\"}";
17671764
}
17681765
break;
17691766
}

packages/react-native/ReactCommon/react/renderer/debug/DebugStringConvertible.cpp

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@
77

88
#include "DebugStringConvertible.h"
99

10-
#include <array>
11-
#include <cinttypes>
12-
#include <cstdio>
13-
14-
#include <double-conversion/double-conversion.h>
10+
#include <cstdint>
11+
#include <iomanip>
12+
#include <sstream>
1513

1614
namespace facebook::react {
1715

@@ -129,33 +127,30 @@ SharedDebugStringConvertibleList DebugStringConvertible::getDebugProps() const {
129127
* `toString`-family implementation.
130128
*/
131129
std::string toString(const double& value) {
132-
// Format taken from folly's toString
133-
static double_conversion::DoubleToStringConverter conv(
134-
0,
135-
"Infinity",
136-
"NaN",
137-
'E',
138-
-6, // detail::kConvMaxDecimalInShortestLow,
139-
21, // detail::kConvMaxDecimalInShortestHigh,
140-
6, // max leading padding zeros
141-
1); // max trailing padding zeros
142-
std::array<char, 256> buffer{};
143-
double_conversion::StringBuilder builder(buffer.data(), buffer.size());
144-
conv.ToShortest(value, &builder);
145-
return builder.Finalize();
130+
std::ostringstream stream;
131+
stream << std::fixed << std::setprecision(4) << value;
132+
std::string result = stream.str();
133+
134+
// Strip trailing zeros and unnecessary decimal point
135+
if (auto dotPos = result.find('.'); dotPos != std::string::npos) {
136+
auto lastNonZero = result.find_last_not_of('0');
137+
if (lastNonZero == dotPos) {
138+
result.erase(dotPos);
139+
} else {
140+
result.erase(lastNonZero + 1);
141+
}
142+
}
143+
return result;
146144
}
147145

148146
std::string toString(const void* value) {
149147
if (value == nullptr) {
150148
return "null";
151149
}
152-
std::array<char, 20> buffer{};
153-
std::snprintf(
154-
buffer.data(),
155-
buffer.size(),
156-
"0x%" PRIXPTR,
157-
reinterpret_cast<uintptr_t>(value));
158-
return buffer.data();
150+
std::ostringstream stream;
151+
stream << "0x" << std::uppercase << std::hex
152+
<< reinterpret_cast<uintptr_t>(value);
153+
return stream.str();
159154
}
160155

161156
#endif

0 commit comments

Comments
 (0)