[libc-commits] [libc] [libc][test] make `str_to_float_comparison_test` independent of C++ headers. (PR #133978)

Michael Jones via libc-commits libc-commits at lists.llvm.org
Tue Apr 8 14:35:56 PDT 2025


================
@@ -54,120 +69,169 @@ static inline uint64_t fastHexToU64(const char *inStr) {
   return result;
 }
 
-int checkFile(char *inputFileName, int *totalFails, int *totalBitDiffs,
-              int *detailedBitDiffs, int *total) {
-  int32_t curFails = 0;    // Only counts actual failures, not bitdiffs.
-  int32_t curBitDiffs = 0; // A bitdiff is when the expected result and actual
-                           // result are off by +/- 1 bit.
-  std::string line;
-  std::string num;
-
-  std::ifstream fileStream(inputFileName, std::ifstream::in);
+static void parseLine(char *line, ParseResult &parseResult, int32_t &curFails,
+                      int32_t &curBitDiffs) {
 
-  if (!fileStream.is_open()) {
-    std::cout << "file '" << inputFileName << "' failed to open. Exiting.\n";
-    return 1;
+  if (line[0] == '#') {
+    return;
   }
-  while (getline(fileStream, line)) {
-    if (line[0] == '#') {
-      continue;
-    }
-    *total = *total + 1;
-    uint32_t expectedFloatRaw;
-    uint64_t expectedDoubleRaw;
+  parseResult.total += 1;
+  uint32_t expectedFloatRaw;
+  uint64_t expectedDoubleRaw;
 
-    expectedFloatRaw = fastHexToU32(line.c_str() + 5);
-    expectedDoubleRaw = fastHexToU64(line.c_str() + 14);
-    num = line.substr(31);
+  expectedFloatRaw = fastHexToU32(line + 5);
+  expectedDoubleRaw = fastHexToU64(line + 14);
 
-    float floatResult = strtof(num.c_str(), nullptr);
+  char *num = line + 31;
 
-    double doubleResult = strtod(num.c_str(), nullptr);
+  float floatResult = LIBC_NAMESPACE::strtof(num, nullptr);
 
-    uint32_t floatRaw = *(uint32_t *)(&floatResult);
+  double doubleResult = LIBC_NAMESPACE::strtod(num, nullptr);
 
-    uint64_t doubleRaw = *(uint64_t *)(&doubleResult);
+  uint32_t floatRaw = LIBC_NAMESPACE::cpp::bit_cast<uint32_t>(floatResult);
 
-    if (!(expectedFloatRaw == floatRaw)) {
-      if (expectedFloatRaw == floatRaw + 1 ||
-          expectedFloatRaw == floatRaw - 1) {
-        curBitDiffs++;
-        if (expectedFloatRaw == floatRaw + 1) {
-          detailedBitDiffs[0] = detailedBitDiffs[0] + 1; // float low
-        } else {
-          detailedBitDiffs[1] = detailedBitDiffs[1] + 1; // float high
-        }
+  uint64_t doubleRaw = LIBC_NAMESPACE::cpp::bit_cast<uint64_t>(doubleResult);
+
+  if (!(expectedFloatRaw == floatRaw)) {
+    if (expectedFloatRaw == floatRaw + 1 || expectedFloatRaw == floatRaw - 1) {
+      curBitDiffs++;
+      if (expectedFloatRaw == floatRaw + 1) {
+        parseResult.detailedBitDiffs[0] =
+            parseResult.detailedBitDiffs[0] + 1; // float low
       } else {
-        curFails++;
-      }
-      if (curFails + curBitDiffs < 10) {
-        std::cout << "Float fail for '" << num << "'. Expected " << std::hex
-                  << expectedFloatRaw << " but got " << floatRaw << "\n"
-                  << std::dec;
+        parseResult.detailedBitDiffs[1] =
+            parseResult.detailedBitDiffs[1] + 1; // float high
       }
+    } else {
+      curFails++;
     }
+    if (curFails + curBitDiffs < 10) {
+      LIBC_NAMESPACE::printf("Float fail for '%s'. Expected %x but got %x\n",
+                             num, expectedFloatRaw, floatRaw);
+    }
+  }
 
-    if (!(expectedDoubleRaw == doubleRaw)) {
-      if (expectedDoubleRaw == doubleRaw + 1 ||
-          expectedDoubleRaw == doubleRaw - 1) {
-        curBitDiffs++;
-        if (expectedDoubleRaw == doubleRaw + 1) {
-          detailedBitDiffs[2] = detailedBitDiffs[2] + 1; // double low
-        } else {
-          detailedBitDiffs[3] = detailedBitDiffs[3] + 1; // double high
-        }
+  if (!(expectedDoubleRaw == doubleRaw)) {
+    if (expectedDoubleRaw == doubleRaw + 1 ||
+        expectedDoubleRaw == doubleRaw - 1) {
+      curBitDiffs++;
+      if (expectedDoubleRaw == doubleRaw + 1) {
+        parseResult.detailedBitDiffs[2] =
+            parseResult.detailedBitDiffs[2] + 1; // double low
       } else {
-        curFails++;
-      }
-      if (curFails + curBitDiffs < 10) {
-        std::cout << "Double fail for '" << num << "'. Expected " << std::hex
-                  << expectedDoubleRaw << " but got " << doubleRaw << "\n"
-                  << std::dec;
+        parseResult.detailedBitDiffs[3] =
+            parseResult.detailedBitDiffs[3] + 1; // double high
       }
+    } else {
+      curFails++;
     }
+    if (curFails + curBitDiffs < 10) {
+      LIBC_NAMESPACE::printf("Double fail for '%s'. Expected %lx but got %lx\n",
+                             num, expectedDoubleRaw, doubleRaw);
+    }
+  }
+}
+
+ParseStatus checkBuffer(ParseResult &parseResult) {
+  constexpr const char *lines[6] = {
+      "3C00 3F800000 3FF0000000000000 1",
+      "3D00 3FA00000 3FF4000000000000 1.25",
+      "3D9A 3FB33333 3FF6666666666666 1.4",
+      "57B7 42F6E979 405EDD2F1A9FBE77 123.456",
+      "622A 44454000 4088A80000000000 789",
+      "7C00 7F800000 7FF0000000000000 123.456e789"};
+
+  int32_t curFails = 0;    // Only counts actual failures, not bitdiffs.
+  int32_t curBitDiffs = 0; // A bitdiff is when the expected result and actual
+  // result are off by +/- 1 bit.
+
+  for (uint8_t i = 0; i < 6; i++) {
+    auto line = const_cast<char *>(lines[i]);
+    parseLine(line, parseResult, curFails, curBitDiffs);
   }
 
-  fileStream.close();
+  parseResult.totalBitDiffs += curBitDiffs;
+  parseResult.totalFails += curFails;
 
-  *totalBitDiffs += curBitDiffs;
-  *totalFails += curFails;
+  if (curFails > 1 || curBitDiffs > 1) {
+    return ParseStatus::PARSE_ERROR;
+  }
+  return ParseStatus::SUCCESS;
+}
+
+ParseStatus checkFile(char *inputFileName, ParseResult &parseResult) {
+  int32_t curFails = 0;    // Only counts actual failures, not bitdiffs.
+  int32_t curBitDiffs = 0; // A bitdiff is when the expected result and actual
+  // result are off by +/- 1 bit.
+  char line[100];
+
+  auto *fileHandle = LIBC_NAMESPACE::fopen(inputFileName, "r");
+
+  if (!fileHandle) {
+    LIBC_NAMESPACE::printf("file '%s' failed to open. Exiting.\n",
+                           inputFileName);
+    return ParseStatus::FILE_ERROR;
+  }
+
+  while (LIBC_NAMESPACE::fgets(line, sizeof(line), fileHandle)) {
+    parseLine(line, parseResult, curFails, curBitDiffs);
+  }
+
+  LIBC_NAMESPACE::fclose(fileHandle);
+
+  parseResult.totalBitDiffs += curBitDiffs;
+  parseResult.totalFails += curFails;
 
   if (curFails > 1 || curBitDiffs > 1) {
-    return 2;
+    return ParseStatus::PARSE_ERROR;
+  }
+  return ParseStatus::SUCCESS;
+}
+
+ParseStatus updateResult(ParseStatus result, ParseStatus curResult) {
+  if (curResult == ParseStatus::FILE_ERROR) {
+    result = ParseStatus::FILE_ERROR;
+  } else if (curResult == ParseStatus::PARSE_ERROR) {
+    result = ParseStatus::PARSE_ERROR;
   }
-  return 0;
+  return result;
 }
 
-int main(int argc, char *argv[]) {
-  int result = 0;
-  int fails = 0;
+TEST(LlvmLibcStrToFloatComparisonTest, CheckFloats) {
+  ParseStatus parseStatus = ParseStatus::SUCCESS;
 
   // Bitdiffs are cases where the expected result and actual result only differ
   // by +/- the least significant bit. They are tracked separately from larger
   // failures since a bitdiff is most likely the result of a rounding error, and
   // splitting them off makes them easier to track down.
-  int bitdiffs = 0;
-  int detailedBitDiffs[4] = {0, 0, 0, 0};
-
-  int total = 0;
-  for (int i = 1; i < argc; i++) {
-    std::cout << "Starting file " << argv[i] << "\n";
-    int curResult =
-        checkFile(argv[i], &fails, &bitdiffs, detailedBitDiffs, &total);
-    if (curResult == 1) {
-      result = 1;
-      break;
-    } else if (curResult == 2) {
-      result = 2;
+
+  ParseResult parseResult = {
+      .totalFails = 0,
+      .totalBitDiffs = 0,
+      .detailedBitDiffs = {0, 0, 0, 0},
+      .total = 0,
+  };
+
+  char *files = LIBC_NAMESPACE::getenv("FILES");
+
+  if (files == nullptr) {
+    ParseStatus curResult = checkBuffer(parseResult);
+    parseStatus = updateResult(parseStatus, curResult);
+  } else {
+    files = LIBC_NAMESPACE::strdup(files);
+    for (char *file = LIBC_NAMESPACE::strtok(files, ","); file != nullptr;
+         file = LIBC_NAMESPACE::strtok(nullptr, ",")) {
+      ParseStatus curResult = checkFile(file, parseResult);
+      parseStatus = updateResult(parseStatus, curResult);
     }
   }
-  std::cout << "Results:\n"
-            << "Total significant failed conversions: " << fails << "\n"
-            << "Total conversions off by +/- 1 bit: " << bitdiffs << "\n"
-            << "\t" << detailedBitDiffs[0] << "\tfloat low\n"
-            << "\t" << detailedBitDiffs[1] << "\tfloat high\n"
-            << "\t" << detailedBitDiffs[2] << "\tdouble low\n"
-            << "\t" << detailedBitDiffs[3] << "\tdouble high\n"
-            << "Total lines: " << total << "\n";
-  return result;
+
+  EXPECT_EQ(parseStatus, ParseStatus::SUCCESS);
+  EXPECT_EQ(parseResult.totalFails, 0u);
+  EXPECT_EQ(parseResult.totalBitDiffs, 0u);
+  EXPECT_EQ(parseResult.detailedBitDiffs[0], 0u); // float low
+  EXPECT_EQ(parseResult.detailedBitDiffs[1], 0u); // float high
+  EXPECT_EQ(parseResult.detailedBitDiffs[2], 0u); // double low
+  EXPECT_EQ(parseResult.detailedBitDiffs[3], 0u); // double high
+  EXPECT_EQ(parseResult.total, 6u);
----------------
michaelrj-google wrote:

This assumes every file has exactly 6 lines. This should probably be changed to `printf("Total lines: %d\n", parseResult.total);`

https://github.com/llvm/llvm-project/pull/133978


More information about the libc-commits mailing list