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/CPP/bit.h"
10#include "src/stdio/fclose.h"
11#include "src/stdio/fgets.h"
12#include "src/stdio/fopen.h"
13#include "src/stdio/printf.h"
14#include "src/stdlib/getenv.h"
15#include "src/stdlib/strtod.h"
16#include "src/stdlib/strtof.h"
17#include "src/string/strdup.h"
18#include "src/string/strtok.h"
19#include "test/UnitTest/Test.h"
20#include <stdint.h>
21
22// The intent of this test is to read in files in the format used in this test
23// dataset: https://github.com/nigeltao/parse-number-fxx-test-data
24// The format is as follows:
25// Hexadecimal representations of IEEE754 floats in 16 bits, 32 bits, and 64
26// bits, then the string that matches to them.
27
28// 3C00 3F800000 3FF0000000000000 1.0
29
30// By default, float_comp_in.txt is used as the test set, but once built this
31// file can be run against the larger test set. To do that, clone the repository
32// with the dataset, then navigate to the compiled binary of this file (it
33// should be in llvm_project/build/bin). Run the following command:
34// ./libc_str_to_float_comparison_test <path/to/dataset/repo>/data/*
35// It will take a few seconds to run.
36
37struct ParseResult {
38 uint32_t totalFails;
39 uint32_t totalBitDiffs;
40 uint32_t detailedBitDiffs[4];
41 uint32_t total;
42};
43
44enum class ParseStatus : uint8_t {
45 SUCCESS,
46 FILE_ERROR,
47 PARSE_ERROR,
48};
49
50static inline uint32_t hexCharToU32(char in) {
51 return in > '9' ? in + 10 - 'A' : in - '0';
52}
53
54// Fast because it assumes inStr points to exactly 8 uppercase hex chars
55static inline uint32_t fastHexToU32(const char *inStr) {
56 uint32_t result = 0;
57 result = (hexCharToU32(in: inStr[0]) << 28) + (hexCharToU32(in: inStr[1]) << 24) +
58 (hexCharToU32(in: inStr[2]) << 20) + (hexCharToU32(in: inStr[3]) << 16) +
59 (hexCharToU32(in: inStr[4]) << 12) + (hexCharToU32(in: inStr[5]) << 8) +
60 (hexCharToU32(in: inStr[6]) << 4) + hexCharToU32(in: inStr[7]);
61 return result;
62}
63
64// Fast because it assumes inStr points to exactly 8 uppercase hex chars
65static inline uint64_t fastHexToU64(const char *inStr) {
66 uint64_t result = 0;
67 result = (static_cast<uint64_t>(fastHexToU32(inStr)) << 32) +
68 fastHexToU32(inStr: inStr + 8);
69 return result;
70}
71
72static void parseLine(const char *line, ParseResult &parseResult,
73 int32_t &curFails, int32_t &curBitDiffs) {
74
75 if (line[0] == '#')
76 return;
77
78 parseResult.total += 1;
79 uint32_t expectedFloatRaw;
80 uint64_t expectedDoubleRaw;
81
82 expectedFloatRaw = fastHexToU32(inStr: line + 5);
83 expectedDoubleRaw = fastHexToU64(inStr: line + 14);
84
85 const char *num = line + 31;
86
87 float floatResult = LIBC_NAMESPACE::strtof(num, nullptr);
88
89 double doubleResult = LIBC_NAMESPACE::strtod(num, nullptr);
90
91 uint32_t floatRaw = LIBC_NAMESPACE::cpp::bit_cast<uint32_t>(floatResult);
92
93 uint64_t doubleRaw = LIBC_NAMESPACE::cpp::bit_cast<uint64_t>(doubleResult);
94
95 if (!(expectedFloatRaw == floatRaw)) {
96 if (expectedFloatRaw == floatRaw + 1 || expectedFloatRaw == floatRaw - 1) {
97 curBitDiffs++;
98 if (expectedFloatRaw == floatRaw + 1) {
99 parseResult.detailedBitDiffs[0] =
100 parseResult.detailedBitDiffs[0] + 1; // float low
101 } else {
102 parseResult.detailedBitDiffs[1] =
103 parseResult.detailedBitDiffs[1] + 1; // float high
104 }
105 } else {
106 curFails++;
107 }
108 if (curFails + curBitDiffs < 10) {
109 LIBC_NAMESPACE::printf("Float fail for '%s'. Expected %x but got %x\n",
110 num, expectedFloatRaw, floatRaw);
111 }
112 }
113
114 if (!(expectedDoubleRaw == doubleRaw)) {
115 if (expectedDoubleRaw == doubleRaw + 1 ||
116 expectedDoubleRaw == doubleRaw - 1) {
117 curBitDiffs++;
118 if (expectedDoubleRaw == doubleRaw + 1) {
119 parseResult.detailedBitDiffs[2] =
120 parseResult.detailedBitDiffs[2] + 1; // double low
121 } else {
122 parseResult.detailedBitDiffs[3] =
123 parseResult.detailedBitDiffs[3] + 1; // double high
124 }
125 } else {
126 curFails++;
127 }
128 if (curFails + curBitDiffs < 10) {
129 LIBC_NAMESPACE::printf("Double fail for '%s'. Expected %lx but got %lx\n",
130 num, expectedDoubleRaw, doubleRaw);
131 }
132 }
133}
134
135ParseStatus checkBuffer(ParseResult &parseResult) {
136 constexpr const char *LINES[] = {
137 "3C00 3F800000 3FF0000000000000 1",
138 "3D00 3FA00000 3FF4000000000000 1.25",
139 "3D9A 3FB33333 3FF6666666666666 1.4",
140 "57B7 42F6E979 405EDD2F1A9FBE77 123.456",
141 "622A 44454000 4088A80000000000 789",
142 "7C00 7F800000 7FF0000000000000 123.456e789"};
143
144 int32_t curFails = 0; // Only counts actual failures, not bitdiffs.
145 int32_t curBitDiffs = 0; // A bitdiff is when the expected result and actual
146 // result are off by +/- 1 bit.
147
148 for (uint8_t i = 0; i < sizeof(LINES) / sizeof(LINES[0]); i++) {
149 parseLine(line: LINES[i], parseResult, curFails, curBitDiffs);
150 }
151
152 parseResult.totalBitDiffs += curBitDiffs;
153 parseResult.totalFails += curFails;
154
155 if (curFails > 1 || curBitDiffs > 1) {
156 return ParseStatus::PARSE_ERROR;
157 }
158 return ParseStatus::SUCCESS;
159}
160
161ParseStatus checkFile(char *inputFileName, ParseResult &parseResult) {
162 int32_t curFails = 0; // Only counts actual failures, not bitdiffs.
163 int32_t curBitDiffs = 0; // A bitdiff is when the expected result and actual
164 // result are off by +/- 1 bit.
165 char line[1000];
166
167 auto *fileHandle = LIBC_NAMESPACE::fopen(inputFileName, "r");
168
169 if (!fileHandle) {
170 LIBC_NAMESPACE::printf("file '%s' failed to open. Exiting.\n",
171 inputFileName);
172 return ParseStatus::FILE_ERROR;
173 }
174
175 while (LIBC_NAMESPACE::fgets(line, sizeof(line), fileHandle)) {
176 parseLine(line, parseResult, curFails, curBitDiffs);
177 }
178
179 LIBC_NAMESPACE::fclose(fileHandle);
180
181 parseResult.totalBitDiffs += curBitDiffs;
182 parseResult.totalFails += curFails;
183
184 if (curFails > 1 || curBitDiffs > 1) {
185 return ParseStatus::PARSE_ERROR;
186 }
187 return ParseStatus::SUCCESS;
188}
189
190ParseStatus updateStatus(ParseStatus parse_status, ParseStatus cur_status) {
191 if (cur_status == ParseStatus::FILE_ERROR) {
192 parse_status = ParseStatus::FILE_ERROR;
193 } else if (cur_status == ParseStatus::PARSE_ERROR) {
194 parse_status = ParseStatus::PARSE_ERROR;
195 }
196 return parse_status;
197}
198
199TEST(LlvmLibcStrToFloatComparisonTest, CheckFloats) {
200 ParseStatus parseStatus = ParseStatus::SUCCESS;
201
202 // Bitdiffs are cases where the expected result and actual result only differ
203 // by +/- the least significant bit. They are tracked separately from larger
204 // failures since a bitdiff is most likely the result of a rounding error, and
205 // splitting them off makes them easier to track down.
206
207 ParseResult parseResult = {
208 .totalFails = 0,
209 .totalBitDiffs = 0,
210 .detailedBitDiffs = {0, 0, 0, 0},
211 .total = 0,
212 };
213
214 char *files = LIBC_NAMESPACE::getenv("FILES");
215
216 if (files == nullptr) {
217 ParseStatus cur_status = checkBuffer(parseResult);
218 parseStatus = updateStatus(parse_status: parseStatus, cur_status);
219 } else {
220 files = LIBC_NAMESPACE::strdup(files);
221 for (char *file = LIBC_NAMESPACE::strtok(files, ","); file != nullptr;
222 file = LIBC_NAMESPACE::strtok(nullptr, ",")) {
223 ParseStatus cur_status = checkFile(inputFileName: file, parseResult);
224 parseStatus = updateStatus(parse_status: parseStatus, cur_status);
225 }
226 }
227
228 EXPECT_EQ(parseStatus, ParseStatus::SUCCESS);
229 EXPECT_EQ(parseResult.totalFails, 0u);
230 EXPECT_EQ(parseResult.totalBitDiffs, 0u);
231 EXPECT_EQ(parseResult.detailedBitDiffs[0], 0u); // float low
232 EXPECT_EQ(parseResult.detailedBitDiffs[1], 0u); // float high
233 EXPECT_EQ(parseResult.detailedBitDiffs[2], 0u); // double low
234 EXPECT_EQ(parseResult.detailedBitDiffs[3], 0u); // double high
235 LIBC_NAMESPACE::printf("Total lines: %d\n", parseResult.total);
236}
237

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of libc/test/src/__support/str_to_float_comparison_test.cpp