1 //===-- strtofloatingpoint comparison test --------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 // #include "src/__support/str_float_conv_utils.h" 10 11 #include <stdlib.h> // For string to float functions 12 13 // #include "src/__support/FPUtil/FPBits.h" 14 15 #include <cstdint> 16 #include <fstream> 17 #include <iostream> 18 #include <string> 19 20 // The intent of this test is to read in files in the format used in this test 21 // dataset: https://github.com/nigeltao/parse-number-fxx-test-data 22 // The format is as follows: 23 // Hexadecimal representations of IEEE754 floats in 16 bits, 32 bits, and 64 24 // bits, then the string that matches to them. 25 26 // 3C00 3F800000 3FF0000000000000 1.0 27 28 // By default, float_comp_in.txt is used as the test set, but once built this 29 // file can be run against the larger test set. To do that, clone the repository 30 // with the dataset, then navigate to the compiled binary of this file (it 31 // should be in llvm_project/build/bin). Run the following command: 32 // ./libc_str_to_float_comparison_test <path/to/dataset/repo>/data/* 33 // It will take a few seconds to run. 34 35 static inline uint32_t hexCharToU32(char in) { 36 return in > '9' ? in + 10 - 'A' : in - '0'; 37 } 38 39 // Fast because it assumes inStr points to exactly 8 uppercase hex chars 40 static inline uint32_t fastHexToU32(const char *inStr) { 41 uint32_t result = 0; 42 result = (hexCharToU32(inStr[0]) << 28) + (hexCharToU32(inStr[1]) << 24) + 43 (hexCharToU32(inStr[2]) << 20) + (hexCharToU32(inStr[3]) << 16) + 44 (hexCharToU32(inStr[4]) << 12) + (hexCharToU32(inStr[5]) << 8) + 45 (hexCharToU32(inStr[6]) << 4) + hexCharToU32(inStr[7]); 46 return result; 47 } 48 49 // Fast because it assumes inStr points to exactly 8 uppercase hex chars 50 static inline uint64_t fastHexToU64(const char *inStr) { 51 uint64_t result = 0; 52 result = (static_cast<uint64_t>(fastHexToU32(inStr)) << 32) + 53 fastHexToU32(inStr + 8); 54 return result; 55 } 56 57 int checkFile(char *inputFileName, int *totalFails, int *totalBitDiffs, 58 int *detailedBitDiffs, int *total) { 59 int32_t curFails = 0; // Only counts actual failures, not bitdiffs. 60 int32_t curBitDiffs = 0; // A bitdiff is when the expected result and actual 61 // result are off by +/- 1 bit. 62 std::string line; 63 std::string num; 64 65 std::ifstream fileStream(inputFileName, std::ifstream::in); 66 67 if (!fileStream.is_open()) { 68 std::cout << "file '" << inputFileName << "' failed to open. Exiting.\n"; 69 return 1; 70 } 71 while (getline(fileStream, line)) { 72 if (line[0] == '#') { 73 continue; 74 } 75 *total = *total + 1; 76 uint32_t expectedFloatRaw; 77 uint64_t expectedDoubleRaw; 78 79 expectedFloatRaw = fastHexToU32(line.c_str() + 5); 80 expectedDoubleRaw = fastHexToU64(line.c_str() + 14); 81 num = line.substr(31); 82 83 float floatResult = strtof(num.c_str(), nullptr); 84 85 double doubleResult = strtod(num.c_str(), nullptr); 86 87 uint32_t floatRaw = *(uint32_t *)(&floatResult); 88 89 uint64_t doubleRaw = *(uint64_t *)(&doubleResult); 90 91 if (!(expectedFloatRaw == floatRaw)) { 92 if (expectedFloatRaw == floatRaw + 1 || 93 expectedFloatRaw == floatRaw - 1) { 94 curBitDiffs++; 95 if (expectedFloatRaw == floatRaw + 1) { 96 detailedBitDiffs[0] = detailedBitDiffs[0] + 1; // float low 97 } else { 98 detailedBitDiffs[1] = detailedBitDiffs[1] + 1; // float high 99 } 100 } else { 101 curFails++; 102 } 103 if (curFails + curBitDiffs < 10) { 104 std::cout << "Float fail for '" << num << "'. Expected " << std::hex 105 << expectedFloatRaw << " but got " << floatRaw << "\n" 106 << std::dec; 107 } 108 } 109 110 if (!(expectedDoubleRaw == doubleRaw)) { 111 if (expectedDoubleRaw == doubleRaw + 1 || 112 expectedDoubleRaw == doubleRaw - 1) { 113 curBitDiffs++; 114 if (expectedDoubleRaw == doubleRaw + 1) { 115 detailedBitDiffs[2] = detailedBitDiffs[2] + 1; // double low 116 } else { 117 detailedBitDiffs[3] = detailedBitDiffs[3] + 1; // double high 118 } 119 } else { 120 curFails++; 121 } 122 if (curFails + curBitDiffs < 10) { 123 std::cout << "Double fail for '" << num << "'. Expected " << std::hex 124 << expectedDoubleRaw << " but got " << doubleRaw << "\n" 125 << std::dec; 126 } 127 } 128 } 129 130 fileStream.close(); 131 132 *totalBitDiffs += curBitDiffs; 133 *totalFails += curFails; 134 135 if (curFails > 1 || curBitDiffs > 1) { 136 return 2; 137 } 138 return 0; 139 } 140 141 int main(int argc, char *argv[]) { 142 int result = 0; 143 int fails = 0; 144 145 // Bitdiffs are cases where the expected result and actual result only differ 146 // by +/- the least significant bit. They are tracked separately from larger 147 // failures since a bitdiff is most likely the result of a rounding error, and 148 // splitting them off makes them easier to track down. 149 int bitdiffs = 0; 150 int detailedBitDiffs[4] = {0, 0, 0, 0}; 151 152 int total = 0; 153 for (int i = 1; i < argc; i++) { 154 std::cout << "Starting file " << argv[i] << "\n"; 155 int curResult = 156 checkFile(argv[i], &fails, &bitdiffs, detailedBitDiffs, &total); 157 if (curResult == 1) { 158 result = 1; 159 break; 160 } else if (curResult == 2) { 161 result = 2; 162 } 163 } 164 std::cout << "Results:\n" 165 << "Total significant failed conversions: " << fails << "\n" 166 << "Total conversions off by +/- 1 bit: " << bitdiffs << "\n" 167 << "\t" << detailedBitDiffs[0] << "\tfloat low\n" 168 << "\t" << detailedBitDiffs[1] << "\tfloat high\n" 169 << "\t" << detailedBitDiffs[2] << "\tdouble low\n" 170 << "\t" << detailedBitDiffs[3] << "\tdouble high\n" 171 << "Total lines: " << total << "\n"; 172 return result; 173 } 174