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