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 <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
35static 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
40static inline uint32_t fastHexToU32(const char *inStr) {
41 uint32_t result = 0;
42 result = (hexCharToU32(in: inStr[0]) << 28) + (hexCharToU32(in: inStr[1]) << 24) +
43 (hexCharToU32(in: inStr[2]) << 20) + (hexCharToU32(in: inStr[3]) << 16) +
44 (hexCharToU32(in: inStr[4]) << 12) + (hexCharToU32(in: inStr[5]) << 8) +
45 (hexCharToU32(in: inStr[6]) << 4) + hexCharToU32(in: inStr[7]);
46 return result;
47}
48
49// Fast because it assumes inStr points to exactly 8 uppercase hex chars
50static inline uint64_t fastHexToU64(const char *inStr) {
51 uint64_t result = 0;
52 result = (static_cast<uint64_t>(fastHexToU32(inStr)) << 32) +
53 fastHexToU32(inStr: inStr + 8);
54 return result;
55}
56
57int 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(is&: fileStream, str&: line)) {
72 if (line[0] == '#') {
73 continue;
74 }
75 *total = *total + 1;
76 uint32_t expectedFloatRaw;
77 uint64_t expectedDoubleRaw;
78
79 expectedFloatRaw = fastHexToU32(inStr: line.c_str() + 5);
80 expectedDoubleRaw = fastHexToU64(inStr: line.c_str() + 14);
81 num = line.substr(pos: 31);
82
83 float floatResult = strtof(nptr: num.c_str(), endptr: nullptr);
84
85 double doubleResult = strtod(nptr: num.c_str(), endptr: 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
141int 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 seperately 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(inputFileName: argv[i], totalFails: &fails, totalBitDiffs: &bitdiffs, detailedBitDiffs, total: &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

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