f678914411ba67fe5b4839d793dcd5c3719e252e
[oota-llvm.git] / utils / fpcmp / fpcmp.cpp
1 //===- fpcmp.cpp - A fuzzy "cmp" that permits floating point noise --------===//
2 // 
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file was developed by the LLVM research group and is distributed under
6 // the University of Illinois Open Source License. See LICENSE.TXT for details.
7 // 
8 //===----------------------------------------------------------------------===//
9 //
10 // fpcmp is a tool that basically works like the 'cmp' tool, except that it can
11 // tolerate errors due to floating point noise, with the -r option.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #include "llvm/Support/CommandLine.h"
16 #include "llvm/System/MappedFile.h"
17 #include <iostream>
18 #include <cmath>
19
20 using namespace llvm;
21
22 namespace {
23   cl::opt<std::string>
24   File1(cl::Positional, cl::desc("<input file #1>"), cl::Required);
25   cl::opt<std::string>
26   File2(cl::Positional, cl::desc("<input file #2>"), cl::Required);
27
28   cl::opt<double>
29   RelTolerance("r", cl::desc("Relative error tolerated"), cl::init(0));
30   cl::opt<double>
31   AbsTolerance("a", cl::desc("Absolute error tolerated"), cl::init(0));
32 }
33
34 static bool isNumberChar(char C) {
35   switch (C) {
36   case '0': case '1': case '2': case '3': case '4':
37   case '5': case '6': case '7': case '8': case '9': 
38   case '.': case '+': case '-':
39   case 'e':
40   case 'E': return true;
41   default: return false;
42   }
43 }
44
45 static char *BackupNumber(char *Pos, char *FirstChar) {
46   // If we didn't stop in the middle of a number, don't backup.
47   if (!isNumberChar(*Pos)) return Pos;
48
49   // Otherwise, return to the start of the number.
50   while (Pos > FirstChar && isNumberChar(Pos[-1]))
51     --Pos;
52   return Pos;
53 }
54
55 static void CompareNumbers(char *&F1P, char *&F2P, char *F1End, char *F2End) {
56   char *F1NumEnd, *F2NumEnd;
57   double V1 = 0.0, V2 = 0.0; 
58   // If we stop on numbers, compare their difference.
59   if (isNumberChar(*F1P) && isNumberChar(*F2P)) {
60     V1 = strtod(F1P, &F1NumEnd);
61     V2 = strtod(F2P, &F2NumEnd);
62   } else {
63     // Otherwise, the diff failed.
64     F1NumEnd = F1P;
65     F2NumEnd = F2P;
66   }
67
68   if (F1NumEnd == F1P || F2NumEnd == F2P) {
69     std::cerr << "Comparison failed, not a numeric difference.\n";
70     exit(1);
71   }
72
73   // Check to see if these are inside the absolute tolerance
74   if (AbsTolerance < std::abs(V1-V2)) {
75     // Nope, check the relative tolerance...
76     double Diff;
77     if (V2)
78       Diff = std::abs(V1/V2 - 1.0);
79     else if (V1)
80       Diff = std::abs(V2/V1 - 1.0);
81     else
82       Diff = 0;  // Both zero.
83     if (Diff > RelTolerance) {
84       std::cerr << "Compared: " << V1 << " and " << V2 << ": diff = "
85                 << Diff << "\n";
86       std::cerr << "Out of tolerance: rel/abs: " << RelTolerance
87                 << "/" << AbsTolerance << "\n";
88       exit(1);
89     }
90   }
91
92   // Otherwise, advance our read pointers to the end of the numbers.
93   F1P = F1NumEnd;  F2P = F2NumEnd;
94 }
95
96 // PadFileIfNeeded - If the files are not identical, we will have to be doing
97 // numeric comparisons in here.  There are bad cases involved where we (i.e.,
98 // strtod) might run off the beginning or end of the file if it starts or ends
99 // with a number.  Because of this, if needed, we pad the file so that it starts
100 // and ends with a null character.
101 static void PadFileIfNeeded(char *&FileStart, char *&FileEnd, char *&FP) {
102   if (isNumberChar(FileStart[0]) || isNumberChar(FileEnd[-1])) {
103     unsigned FileLen = FileEnd-FileStart;
104     char *NewFile = new char[FileLen+2];
105     NewFile[0] = 0;              // Add null padding
106     NewFile[FileLen+1] = 0;      // Add null padding
107     memcpy(NewFile+1, FileStart, FileLen);
108     FP = NewFile+(FP-FileStart)+1;
109     FileStart = NewFile+1;
110     FileEnd = FileStart+FileLen;
111   }
112 }
113
114 int main(int argc, char **argv) {
115   cl::ParseCommandLineOptions(argc, argv);
116
117   try {
118     // map in the files into memory
119     sys::Path F1Path(File1);
120     sys::Path F2Path(File2);
121     sys::MappedFile F1 ( F1Path );
122     sys::MappedFile F2 ( F2Path );
123     F1.map();
124     F2.map();
125
126     // Okay, now that we opened the files, scan them for the first difference.
127     char *File1Start = F1.charBase();
128     char *File2Start = F2.charBase();
129     char *File1End = File1Start+F1.size();
130     char *File2End = File2Start+F2.size();
131     char *F1P = File1Start;
132     char *F2P = File2Start;
133
134     // Scan for the end of file or first difference.
135     while (F1P < File1End && F2P < File2End && *F1P == *F2P)
136       ++F1P, ++F2P;
137
138     // Common case: identifical files.
139     if (F1P == File1End && F2P == File2End) return 0;
140
141     // If the files need padding, do so now.
142     PadFileIfNeeded(File1Start, File1End, F1P);
143     PadFileIfNeeded(File2Start, File2End, F2P);
144     
145     while (1) {
146       // Scan for the end of file or next difference.
147       while (F1P < File1End && F2P < File2End && *F1P == *F2P)
148         ++F1P, ++F2P;
149
150       if (F1P >= File1End || F2P >= File2End) break;
151
152       // Okay, we must have found a difference.  Backup to the start of the
153       // current number each stream is at so that we can compare from the
154       // beginning.
155       F1P = BackupNumber(F1P, File1Start);
156       F2P = BackupNumber(F2P, File2Start);
157
158       // Now that we are at the start of the numbers, compare them, exiting if
159       // they don't match.
160       CompareNumbers(F1P, F2P, File1End, File2End);
161     }
162
163     // Okay, we reached the end of file.  If both files are at the end, we
164     // succeeded.
165     bool F1AtEnd = F1P >= File1End;
166     bool F2AtEnd = F2P >= File2End;
167     if (F1AtEnd & F2AtEnd) return 0;
168
169     // Else, we might have run off the end due to a number: backup and retry.
170     if (F1AtEnd && isNumberChar(F1P[-1])) --F1P;
171     if (F2AtEnd && isNumberChar(F2P[-1])) --F2P;
172     F1P = BackupNumber(F1P, File1Start);
173     F2P = BackupNumber(F2P, File2Start);
174
175     // Now that we are at the start of the numbers, compare them, exiting if
176     // they don't match.
177     CompareNumbers(F1P, F2P, File1End, File2End);
178
179     // If we found the end, we succeeded.
180     if (F1P >= File1End && F2P >= File2End) return 0;
181
182   } catch (const std::string& msg) {
183     std::cerr << argv[0] << ": error: " << msg << "\n";
184     return 2;
185   }
186
187   return 1;
188 }
189