-
Notifications
You must be signed in to change notification settings - Fork 3
/
mydecquad_run_cowlishaw_test.go
397 lines (302 loc) · 12.8 KB
/
mydecquad_run_cowlishaw_test.go
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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
package decnum
import (
"bufio"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
)
const DEBUG_PRINT_PROCESSED_LINES bool = false // ##### set to true if you want to list all the lines in test files that have been processed #####
// This function runs the test files in cowlishaw_test_files/ directory.
// These test files are provided with the original C decNumber package, and have been downloaded from http://speleotrove.com/decimal, topic "Testcases" (http://speleotrove.com/decimal/dectest.zip).
// Only the test files correponding to Quad type, and for operations that have been implemented in our Go decnum package, are run.
//
func Test_cowlishaw(t *testing.T) {
var (
current_rounding RoundingMode = RoundHalfEven
)
dir := "cowlishaw_test_files"
filename_list := []string{"dqMinus.decTest", "dqAdd.decTest", "dqSubtract.decTest", "dqMultiply.decTest", "dqDivide.decTest", "dqDivideInt.decTest", "dqRemainder.decTest", "dqAbs.decTest", "dqToIntegral.decTest", "dqQuantize.decTest", "dqCompare.decTest", "dqMax.decTest", "dqMin.decTest"}
for _, file_path := range filename_list {
fmt.Printf("--- processing %s ---\n", file_path)
inputFile, err := os.Open(filepath.Join(dir, file_path))
if err != nil {
t.Fatal("Error opening input file:", err)
}
defer inputFile.Close()
scanner := bufio.NewScanner(inputFile)
for scanner.Scan() {
process_line(t, ¤t_rounding, file_path, scanner.Text()) // current_rounding can be modified if "rounding" directive is found
}
if err := scanner.Err(); err != nil {
t.Fatal(scanner.Err())
}
}
}
func process_line(t *testing.T, current_rounding *RoundingMode, file_path string, line_original string) {
// analyze line
line := strings.TrimSpace(line_original)
if line == "" || // line is empty
strings.HasPrefix(line, "version") || // line is a directive we don't use
strings.HasPrefix(line, "extended") ||
strings.HasPrefix(line, "clamp") ||
strings.HasPrefix(line, "precision") ||
strings.HasPrefix(line, "maxExponent") ||
strings.HasPrefix(line, "minExponent") ||
strings.HasPrefix(line, "--") || // line is comment
strings.Contains(line, "#") { // we don't process line with #, tests are too specific
return
}
// if rounding directive, set rounding
if strings.HasPrefix(line, "rounding") {
ss := strings.Split(line, ":")
if len(ss) != 2 {
t.Fatal("Bad 'rounding' directive in test file %s for line %s", file_path, line_original)
}
rounding_mode_string := strings.TrimSpace(ss[1])
switch rounding_mode_string {
case "ceiling":
*current_rounding = RoundCeiling
case "down":
*current_rounding = RoundDown
case "floor":
*current_rounding = RoundFloor
case "half_down":
*current_rounding = RoundHalfDown
case "half_even":
*current_rounding = RoundHalfEven
case "half_up":
*current_rounding = RoundHalfUp
case "up":
*current_rounding = RoundUp
case "05up":
*current_rounding = Round05Up
default:
t.Fatalf("Unknown rounding mode %s", rounding_mode_string)
}
return
}
// get fields from line
fields := strings.Fields(line)
test_name := fields[0]
_ = test_name
test_operator := fields[1]
switch test_operator {
case "apply": // skip, do nothing
return
case "minus":
process_operation_1_operand(t, Quad.Neg, fields, file_path, line_original, *current_rounding)
case "add":
if *current_rounding != RoundHalfEven {
return
}
process_operation_2_operands(t, Quad.Add, fields, file_path, line_original, *current_rounding)
case "subtract":
if *current_rounding != RoundHalfEven {
return
}
process_operation_2_operands(t, Quad.Sub, fields, file_path, line_original, *current_rounding)
case "multiply":
process_operation_2_operands(t, Quad.Mul, fields, file_path, line_original, *current_rounding)
case "divide":
process_operation_2_operands(t, Quad.Div, fields, file_path, line_original, *current_rounding)
case "divideint":
process_operation_2_operands(t, Quad.DivInt, fields, file_path, line_original, *current_rounding)
case "remainder":
process_operation_2_operands(t, Quad.Mod, fields, file_path, line_original, *current_rounding)
case "abs":
process_operation_1_operand(t, Quad.Abs, fields, file_path, line_original, *current_rounding)
case "tointegralx": // status is not checked because "The DEC_Inexact flag is not set by decQuadToIntegralValue, even if rounding ocurred".
a := must_from_string(t, fields[2], file_path, line_original)
if fields[3] != "->" {
t.Fatalf("Bad -> in test file %s for line %s", file_path, line_original)
}
expected_result := must_from_string(t, fields[4], file_path, line_original)
r := a.ToIntegral(*current_rounding)
if r.QuadToString() != expected_result.QuadToString() {
t.Fatalf("Test failed in test file %s for line %s. Result %s != %s. Rounding mode is %s.", file_path, line_original, r.QuadToString(), expected_result, *current_rounding)
}
case "quantize":
process_operation_2_operands_and_rounding(t, Quad.Quantize, fields, file_path, line_original, *current_rounding)
case "compare":
var err error
var expected_result_int32 int32 = 123456 // initialize with invalid value
a := must_from_string(t, fields[2], file_path, line_original)
b := must_from_string(t, fields[3], file_path, line_original)
if fields[4] != "->" {
t.Fatalf("Bad -> in test file %s for line %s", file_path, line_original)
}
expected_result := must_from_string(t, fields[5], file_path, line_original)
r_greater := a.Greater(b)
r_greater_equal := a.GreaterEqual(b)
r_equal := a.Equal(b)
r_less_equal := a.LessEqual(b)
r_less := a.Less(b)
if expected_result.IsNaN() == false {
if expected_result_int32, err = expected_result.ToInt32(RoundHalfEven); err != nil {
panic("impossible")
}
}
failed_flag := false
switch {
case expected_result.IsNaN():
if !(r_greater == false && r_greater_equal == false && r_equal == false && r_less_equal == false && r_less == false) {
failed_flag = true
}
case expected_result_int32 == -1:
if !(r_greater == false && r_greater_equal == false && r_equal == false && r_less_equal == true && r_less == true) {
failed_flag = true
}
case expected_result_int32 == 0:
if !(r_greater == false && r_greater_equal == true && r_equal == true && r_less_equal == true && r_less == false) {
failed_flag = true
}
case expected_result_int32 == 1:
if !(r_greater == true && r_greater_equal == true && r_equal == false && r_less_equal == false && r_less == false) {
failed_flag = true
}
default:
t.Fatal("impossible")
}
if failed_flag {
t.Fatalf("Test failed in test file %s for line %s", file_path, line_original)
}
case "max":
process_operation_2_operands(t, Max, fields, file_path, line_original, *current_rounding)
case "min":
process_operation_2_operands(t, Min, fields, file_path, line_original, *current_rounding)
default:
t.Fatalf("Unknown operator in test file %s for line %s", file_path, line_original)
}
if DEBUG_PRINT_PROCESSED_LINES {
fmt.Printf("%-20s %s\n", *current_rounding, line_original)
}
}
func process_operation_1_operand(t *testing.T, f func(Quad) Quad, fields []string, file_path string, line_original string, rounding_mode RoundingMode) {
a := must_from_string(t, fields[2], file_path, line_original)
if fields[3] != "->" {
t.Fatalf("Bad -> in test file %s for line %s", file_path, line_original)
}
expected_result := must_from_string(t, fields[4], file_path, line_original)
r := f(a)
if r.QuadToString() != expected_result.QuadToString() {
t.Fatalf("Test failed in test file %s for line %s. Result %s != %s. Rounding mode is %s.", file_path, line_original, r.QuadToString(), expected_result, rounding_mode)
}
expected_status := get_expected_status(fields[5:])
if r.Status() != expected_status {
t.Fatalf("Test failed in test file %s for line %s. Status %s != %s. Rounding mode is %s.", file_path, line_original, r.Status(), expected_status, rounding_mode)
}
}
func process_operation_1_operand_and_rounding(t *testing.T, f func(Quad, RoundingMode) Quad, fields []string, file_path string, line_original string, rounding_mode RoundingMode) {
a := must_from_string(t, fields[2], file_path, line_original)
if fields[3] != "->" {
t.Fatalf("Bad -> in test file %s for line %s", file_path, line_original)
}
expected_result := must_from_string(t, fields[4], file_path, line_original)
r := f(a, rounding_mode)
if r.QuadToString() != expected_result.QuadToString() {
t.Fatalf("Test failed in test file %s for line %s. Result %s != %s. Rounding mode is %s.", file_path, line_original, r.QuadToString(), expected_result, rounding_mode)
}
expected_status := get_expected_status(fields[5:])
if r.Status() != expected_status {
t.Fatalf("Test failed in test file %s for line %s. Status %s != %s. Rounding mode is %s.", file_path, line_original, r.Status(), expected_status, rounding_mode)
}
}
func process_operation_2_operands(t *testing.T, f func(Quad, Quad) Quad, fields []string, file_path string, line_original string, rounding_mode RoundingMode) {
a := must_from_string(t, fields[2], file_path, line_original)
b := must_from_string(t, fields[3], file_path, line_original)
if fields[4] != "->" {
t.Fatalf("Bad -> in test file %s for line %s", file_path, line_original)
}
expected_result := must_from_string(t, fields[5], file_path, line_original)
r := f(a, b)
if r.QuadToString() != expected_result.QuadToString() {
t.Fatalf("Test failed in test file %s for line %s. Result %s != %s. Rounding mode is %s.", file_path, line_original, r.QuadToString(), expected_result, rounding_mode)
}
expected_status := get_expected_status(fields[6:])
if r.Status() != expected_status {
t.Fatalf("Test failed in test file %s for line %s. Status %s != %s. Rounding mode is %s.", file_path, line_original, r.Status(), expected_status, rounding_mode)
}
}
func process_operation_2_operands_and_rounding(t *testing.T, f func(Quad, Quad, RoundingMode) Quad, fields []string, file_path string, line_original string, rounding_mode RoundingMode) {
a := must_from_string(t, fields[2], file_path, line_original)
b := must_from_string(t, fields[3], file_path, line_original)
if fields[4] != "->" {
t.Fatalf("Bad -> in test file %s for line %s", file_path, line_original)
}
expected_result := must_from_string(t, fields[5], file_path, line_original)
r := f(a, b, rounding_mode)
if r.QuadToString() != expected_result.QuadToString() {
t.Fatalf("Test failed in test file %s for line %s. Result %s != %s. Rounding mode is %s.", file_path, line_original, r.QuadToString(), expected_result, rounding_mode)
}
expected_status := get_expected_status(fields[6:])
if r.Status() != expected_status {
t.Fatalf("Test failed in test file %s for line %s. Status %s != %s. Rounding mode is %s.", file_path, line_original, r.Status(), expected_status, rounding_mode)
}
}
// converts a string into a Quad.
// It is a fatal error if string is invalid, which should never happen with the test files we have.
//
func must_from_string(t *testing.T, s string, file_path string, line_original string) Quad {
if len(s) > 0 && s[0] == '\'' { // delete opening quote if any
s = s[1:]
assert(s[len(s)-1] == '\'') // delete closing quote
s = s[:len(s)-1]
}
q, _ := FromString(s)
if q.Error() != nil {
t.Fatalf("Test failed in test file %s for line %s. must_from_string(%s) failed. %s", file_path, line_original, s, q.Error())
}
// we take this occasion to also test the conversion string --> Quad --> string --> Quad
q2, _ := FromString(q.String())
if q2.Error() != nil {
t.Fatalf("Test failed in test file %s for line %s. must_from_string(%s) failed. %s", file_path, line_original, s, q2.Error())
}
if q2.QuadToString() != q.QuadToString() {
t.Fatalf("q2 %s != q %s %v %v", q2, q, q2.QuadToString(), q.QuadToString())
}
return q
}
// return a status value with bits set as described by flags argument.
// If "--" is encountered, it is the start of a comment, and the function stops parsing flags.
//
func get_expected_status(flags []string) Status {
var status Status
for _, flag := range flags {
if strings.HasPrefix(flag, "--") { // comment, no more flags on the line
return status
}
switch flag {
case "Conversion_syntax":
status |= ConversionSyntax
case "Division_by_zero":
status |= DivisionByZero
case "Division_impossible":
status |= DivisionImpossible
case "Division_undefined":
status |= DivisionUndefined
case "Insufficient_storage":
status |= InsufficientStorage
case "Inexact":
status |= Inexact
case "Invalid_context":
status |= InvalidContext
case "Invalid_operation":
status |= InvalidOperation
case "Overflow":
status |= Overflow
case "Clamped":
// status |= Clamped
case "Rounded":
// status |= Rounded
case "Subnormal":
// status |= Subnormal
case "Underflow":
status |= Underflow
default:
panic("Unknown status flag: " + flag)
}
}
return status
}