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