]> sourceware.org Git - glibc.git/blame - math/gen-libm-test.pl
Update.
[glibc.git] / math / gen-libm-test.pl
CommitLineData
8847214f
UD
1#!/usr/bin/perl -w
2
3# Copyright (C) 1999 Free Software Foundation, Inc.
4# This file is part of the GNU C Library.
5# Contributed by Andreas Jaeger <aj@suse.de>, 1999.
6
7# The GNU C Library is free software; you can redistribute it and/or
8# modify it under the terms of the GNU Library General Public License as
9# published by the Free Software Foundation; either version 2 of the
10# License, or (at your option) any later version.
11
12# The GNU C Library is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15# Library General Public License for more details.
16
17# You should have received a copy of the GNU Library General Public
18# License along with the GNU C Library; see the file COPYING.LIB. If not,
19# write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20# Boston, MA 02111-1307, USA.
21
22# This file needs to be tidied up
23# Note that functions and tests share the same namespace.
24
25use Getopt::Std;
26
27use strict;
28
29use vars qw ($input $output);
30use vars qw (@tests @functions);
31use vars qw ($count);
32use vars qw (%ulps %failures);
33use vars qw (%beautify);
34use vars qw ($output_dir $ulps_file);
35
fe559c5e 36%beautify =
8847214f
UD
37 ( "minus_zero" => "-0",
38 "plus_zero" => "+0",
39 "minus_infty" => "-inf",
40 "plus_infty" => "inf",
41 "nan_value" => "NaN",
42 "M_El" => "e",
43 "M_E2l" => "e^2",
44 "M_E3l" => "e^3",
45 "M_LOG10El", "log10(e)",
46 "M_PIl" => "pi",
47 "M_PI_34l" => "3/4 pi",
48 "M_PI_2l" => "pi/2",
49 "M_PI_4l" => "pi/4",
50 "M_PI_6l" => "pi/6",
51 "M_PI_34_LOG10El" => "3/4 pi*log10(e)",
52 "M_PI_LOG10El" => "pi*log10(e)",
53 "M_PI2_LOG10El" => "pi/2*log10(e)",
54 "M_PI4_LOG10El" => "pi/4*log10(e)",
55 "M_LOG_SQRT_PIl" => "log(sqrt(pi))",
56 "M_LOG_2_SQRT_PIl" => "log(2*sqrt(pi))",
57 "M_2_SQRT_PIl" => "2 sqrt (pi)",
58 "M_SQRT_PIl" => "sqrt (pi)",
59 "INVALID_EXCEPTION" => "invalid exception",
60 "DIVIDE_BY_ZERO_EXCEPTION" => "division by zero exception",
61 "INVALID_EXCEPTION_OK" => "invalid exception allowed",
62 "DIVIDE_BY_ZERO_EXCEPTION_OK" => "division by zero exception allowed",
63 "EXCEPTIONS_OK" => "exceptions allowed",
64 "IGNORE_ZERO_INF_SIGN" => "sign of zero/inf not specified",
65"INVALID_EXCEPTION|IGNORE_ZERO_INF_SIGN" => "invalid exception and sign of zero/inf not specified"
66 );
67
68
69# get Options
70# Options:
71# u: ulps-file
72# h: help
73# o: output-directory
74# n: generate new ulps file
75use vars qw($opt_u $opt_h $opt_o $opt_n);
76getopts('u:o:nh');
77
78$ulps_file = 'libm-test-ulps';
79$output_dir = '';
80
81if ($opt_h) {
82 print "Usage: generate.pl [OPTIONS]\n";
83 print " -h print this help, then exit\n";
84 print " -o DIR directory where generated files will be placed\n";
85 print " -n generate sorted file NewUlps from libm-test-ulps\n";
86 print " -u FILE input file with ulps\n";
87 exit 0;
88}
89
90$ulps_file = $opt_u if ($opt_u);
91$output_dir = $opt_o if ($opt_o);
92
93$input = "libm-test.inc";
94$output = "${output_dir}libm-test.c";
95
96$count = 0;
97
98&parse_ulps ($ulps_file);
99&generate_testfile ($input, $output);
fe559c5e 100&output_ulps ("${output_dir}libm-test-ulps.h", $ulps_file);
8847214f
UD
101&print_ulps_file ("${output_dir}NewUlps") if ($opt_n);
102
103# Return a nicer representation
104sub beautify {
105 my ($arg) = @_;
106 my ($tmp);
107
108 if (exists $beautify{$arg}) {
109 return $beautify{$arg};
110 }
111 if ($arg =~ /^-/) {
112 $tmp = $arg;
113 $tmp =~ s/^-//;
114 if (exists $beautify{$tmp}) {
115 return '-' . $beautify{$tmp};
116 }
117 }
118 if ($arg =~ /[0-9]L$/) {
119 $arg =~ s/L$//;
120 }
121 return $arg;
122}
123
124# Return a nicer representation of a complex number
125sub build_complex_beautify {
126 my ($r, $i) = @_;
127 my ($str1, $str2);
128
129 $str1 = &beautify ($r);
130 $str2 = &beautify ($i);
131 if ($str2 =~ /^-/) {
132 $str2 =~ s/^-//;
133 $str1 .= ' - ' . $str2;
134 } else {
135 $str1 .= ' + ' . $str2;
136 }
137 $str1 .= ' i';
138 return $str1;
139}
140
141# Return name of a variable
142sub get_variable {
143 my ($number) = @_;
144
fe559c5e
UD
145 return "x" if ($number == 1);
146 return "y" if ($number == 2);
8847214f
UD
147 return "z" if ($number == 3);
148 # return x1,x2,...
149 $number =-3;
150 return "x$number";
151}
152
153# Add a new test to internal data structures and fill in the
154# ulps, failures and exception information for the C line.
155sub new_test {
156 my ($test, $exception) = @_;
157 my $rest;
158
159 # Add ulp, xfail
160 if (exists $ulps{$test}) {
161 $rest = ", DELTA$count";
162 } else {
163 $rest = ', 0';
164 }
165 if (exists $failures{$test}) {
166 $rest .= ", FAIL$count";
167 } else {
168 $rest .= ', 0';
169 }
170 if (defined $exception) {
171 $rest .= ", $exception";
172 } else {
173 $rest .= ', 0';
174 }
175 $rest .= ");\n";
176 # We must increment here to keep @tests and count in sync
177 push @tests, $test;
178 ++$count;
179 return $rest;
180}
181
182# Treat some functions especially.
183# Currently only sincos needs extra treatment.
184sub special_functions {
185 my ($file, $args) = @_;
186 my (@args, $str, $test, $cline);
187
188 @args = split /,\s*/, $args;
fe559c5e 189
8847214f
UD
190 unless ($args[0] =~ /sincos/) {
191 die ("Don't know how to handle $args[0] extra.");
192 }
193 print $file " FUNC (sincos) ($args[1], &sin_res, &cos_res);\n";
194
195 $str = 'sincos (' . &beautify ($args[1]) . ', &sin_res, &cos_res)';
196 # handle sin
197 $test = $str . ' puts ' . &beautify ($args[2]) . ' in sin_res';
198 if ($#args == 4) {
199 $test .= " plus " . &beautify ($args[4]);
200 }
201
202 $cline = " check_float (\"$test\", sin_res, $args[2]";
203 $cline .= &new_test ($test, $args[4]);
204 print $file $cline;
fe559c5e 205
8847214f
UD
206 # handle cos
207 $test = $str . ' puts ' . &beautify ($args[3]) . ' in cos_res';
208 $cline = " check_float (\"$test\", cos_res, $args[3]";
209 # only tests once for exception
210 $cline .= &new_test ($test, undef);
211 print $file $cline;
212}
213
214# Parse the arguments to TEST_x_y
215sub parse_args {
216 my ($file, $descr, $args) = @_;
217 my (@args, $str, $descr_args, $descr_res, @descr);
218 my ($current_arg, $cline, $i);
219 my ($pre, $post, @special);
220 my ($extra_var, $call, $c_call);
221
222 if ($descr eq 'extra') {
223 &special_functions ($file, $args);
224 return;
225 }
226 ($descr_args, $descr_res) = split /_/,$descr, 2;
227
228 @args = split /,\s*/, $args;
229
230 $call = "$args[0] (";
231
232 # Generate first the string that's shown to the user
233 $current_arg = 1;
234 $extra_var = 0;
235 @descr = split //,$descr_args;
236 for ($i = 0; $i <= $#descr; $i++) {
237 if ($i >= 1) {
238 $call .= ', ';
239 }
240 # FLOAT, int, long int, long long int
241 if ($descr[$i] =~ /f|i|l|L/) {
242 $call .= &beautify ($args[$current_arg]);
243 ++$current_arg;
244 next;
245 }
246 # &FLOAT, &int - argument is added here
247 if ($descr[$i] =~ /F|I/) {
248 ++$extra_var;
249 $call .= '&' . &get_variable ($extra_var);
250 next;
251 }
252 # complex
253 if ($descr[$i] eq 'c') {
254 $call .= &build_complex_beautify ($args[$current_arg], $args[$current_arg+1]);
255 $current_arg += 2;
256 next;
257 }
258
259 die ("$descr[$i] is unknown");
260 }
261 $call .= ')';
262 $str = "$call == ";
263
fe559c5e 264 # Result
8847214f
UD
265 @descr = split //,$descr_res;
266 foreach (@descr) {
267 if ($_ =~ /f|i|l|L/) {
268 $str .= &beautify ($args[$current_arg]);
269 ++$current_arg;
270 } elsif ($_ eq 'c') {
271 $str .= &build_complex_beautify ($args[$current_arg], $args[$current_arg+1]);
272 $current_arg += 2;
273 } elsif ($_ eq 'b') {
274 # boolean
275 $str .= ($args[$current_arg] == 0) ? "false" : "true";
276 ++$current_arg;
277 } elsif ($_ eq '1') {
278 ++$current_arg;
279 } else {
280 die ("$_ is unknown");
281 }
282 }
283 # consistency check
284 if ($current_arg == $#args) {
285 die ("wrong number of arguments")
286 unless ($args[$current_arg] =~ /EXCEPTION|IGNORE_ZERO_INF_SIGN/);
287 } elsif ($current_arg < $#args) {
288 die ("wrong number of arguments");
289 } elsif ($current_arg > ($#args+1)) {
290 die ("wrong number of arguments");
291 }
292
293
294 # check for exceptions
295 if ($current_arg <= $#args) {
296 $str .= " plus " . &beautify ($args[$current_arg]);
297 }
298
299 # Put the C program line together
300 # Reset some variables to start again
301 $current_arg = 1;
302 $extra_var = 0;
303 if (substr($descr_res,0,1) eq 'f') {
304 $cline = 'check_float'
305 } elsif (substr($descr_res,0,1) eq 'b') {
306 $cline = 'check_bool';
307 } elsif (substr($descr_res,0,1) eq 'c') {
308 $cline = 'check_complex';
309 } elsif (substr($descr_res,0,1) eq 'i') {
310 $cline = 'check_int';
311 } elsif (substr($descr_res,0,1) eq 'l') {
312 $cline = 'check_long';
313 } elsif (substr($descr_res,0,1) eq 'L') {
314 $cline = 'check_longlong';
315 }
316 # Special handling for some macros:
317 $cline .= " (\"$str\", ";
318 if ($args[0] =~ /fpclassify|isnormal|isfinite|signbit/) {
319 $c_call = "$args[0] (";
320 } else {
321 $c_call = " FUNC($args[0]) (";
322 }
323 @descr = split //,$descr_args;
324 for ($i=0; $i <= $#descr; $i++) {
325 if ($i >= 1) {
326 $c_call .= ', ';
327 }
328 # FLOAT, int, long int, long long int
329 if ($descr[$i] =~ /f|i|l|L/) {
330 $c_call .= $args[$current_arg];
331 $current_arg++;
332 next;
333 }
334 # &FLOAT, &int
335 if ($descr[$i] =~ /F|I/) {
336 ++$extra_var;
337 $c_call .= '&' . &get_variable ($extra_var);
338 next;
339 }
340 # complex
341 if ($descr[$i] eq 'c') {
342 $c_call .= "BUILD_COMPLEX ($args[$current_arg], $args[$current_arg+1])";
343 $current_arg += 2;
344 next;
345 }
346 }
347 $c_call .= ')';
348 $cline .= "$c_call, ";
349
350 @descr = split //,$descr_res;
351 foreach (@descr) {
352 if ($_ =~ /b|f|i|l|L/ ) {
353 $cline .= $args[$current_arg];
354 $current_arg++;
355 } elsif ($_ eq 'c') {
356 $cline .= "BUILD_COMPLEX ($args[$current_arg], $args[$current_arg+1])";
357 $current_arg += 2;
358 } elsif ($_ eq '1') {
359 push @special, $args[$current_arg];
360 ++$current_arg;
361 }
362 }
363 # Add ulp, xfail
364 $cline .= &new_test ($str, ($current_arg <= $#args) ? $args[$current_arg] : undef);
365
366 # special treatment for some functions
367 if ($args[0] eq 'frexp') {
368 if (defined $special[0] && $special[0] ne "IGNORE") {
369 my ($str) = "$call sets x to $special[0]";
370 $post = " check_int (\"$str\", x, $special[0]";
371 $post .= &new_test ($str, undef);
372 }
373 } elsif ($args[0] eq 'gamma' || $args[0] eq 'lgamma') {
374 $pre = " signgam = 0;\n";
375 if (defined $special[0] && $special[0] ne "IGNORE") {
376 my ($str) = "$call sets signgam to $special[0]";
377 $post = " check_int (\"$str\", signgam, $special[0]";
378 $post .= &new_test ($str, undef);
379 }
380 } elsif ($args[0] eq 'modf') {
381 if (defined $special[0] && $special[0] ne "IGNORE") {
382 my ($str) = "$call sets x to $special[0]";
383 $post = " check_float (\"$str\", x, $special[0]";
384 $post .= &new_test ($str, undef);
fe559c5e 385 }
8847214f
UD
386 } elsif ($args[0] eq 'remquo') {
387 if (defined $special[0] && $special[0] ne "IGNORE") {
388 my ($str) = "$call sets x to $special[0]";
389 $post = " check_int (\"$str\", x, $special[0]";
390 $post .= &new_test ($str, undef);
391 }
392 }
fe559c5e 393
8847214f
UD
394 print $file $pre if (defined $pre);
395
396 print $file " $cline\n";
397
398 print $file $post if (defined $post);
399}
400
401# Generate libm-test.c
402sub generate_testfile {
403 my ($input, $output) = @_;
404 my ($lasttext);
405 my (@args, $i, $str);
406
407 open INPUT, $input or die ("Can't open $input: $!");
408 open OUTPUT, ">$output" or die ("Can't open $output: $!");
409
410 # Replace the special macros
411 while (<INPUT>) {
412
413 # TEST_...
414 if (/^\s*TEST_/) {
415 my ($descr, $args);
416 chop;
417 ($descr, $args) = ($_ =~ /TEST_(\w+)\s*\((.*)\)/);
418 &parse_args (\*OUTPUT, $descr, $args);
419 next;
420 }
421 # START (function)
422 if (/START/) {
423 print OUTPUT " init_max_error ();\n";
424 next;
425 }
426 # END (function)
427 if (/END/) {
428 my ($fct, $line);
429 ($fct) = ($_ =~ /END\s*\((.*)\)/);
430 $line = " print_max_error (\"$fct\", ";
431 if (exists $ulps{$fct}) {
432 $line .= "DELTA$fct";
433 } else {
434 $line .= '0';
435 }
436 if (exists $failures{$fct}) {
437 $line .= ", FAIL$fct";
438 } else {
439 $line .= ', 0';
440 }
441 $line .= ");\n";
442 print OUTPUT $line;
443 push @functions, $fct;
444 next;
445 }
446 print OUTPUT;
447 }
448 close INPUT;
449 close OUTPUT;
450}
451
452
453
454# Parse ulps file
455sub parse_ulps {
456 my ($file) = @_;
457 my ($test, $type, $eps);
458
459 open ULP, $file or die ("Can't open $file: $!");
460 while (<ULP>) {
461 chop;
462 # ignore comments and empty lines
463 next if /^#/;
464 next if /^\s*$/;
465 if (/^Test/) {
466 s/^.+\"(.*)\".*$/$1/;
467 $test = $_;
468 next;
469 }
470 if (/^Function/) {
471 ($test) = ($_ =~ /^Function\s*\"([a-zA-Z0-9_]+)\"/);
472 next;
473 }
474 if (/^i?(float|double|ldouble):/) {
475 ($type, $eps) = split /\s*:\s*/,$_,2;
476 if ($eps eq "fail") {
477 $failures{$test}{$type} = 1;
478 } else {
479 $ulps{$test}{$type} = $eps;
480 }
481 next;
482 }
483 print "Skipping unknown entry: `$_'\n";
484 }
485 close ULP;
486}
487
488# Just for testing: Print all ulps
489sub print_ulps {
490 my ($test, $type, $eps);
491
492 foreach $test (keys %ulps) {
493 print "$test:\n";
494 foreach $type (keys %{$ulps{$test}}) {
495 print "$test: $type $ulps{$test}{$type}\n";
496 }
497 }
498}
499
500# Clean up a floating point number
501sub clean_up_number {
502 my ($number) = @_;
fe559c5e 503
8847214f
UD
504 # Remove trailing zeros
505 $number =~ s/0+$//;
506 $number =~ s/\.$//;
507 return $number;
508}
509
510# Output a file which can be read in as ulps file.
511sub print_ulps_file {
512 my ($file) = @_;
513 my ($test, $type, $eps, $fct, $last_fct);
514
515 $last_fct = '';
516 open NEWULP, ">$file" or die ("Can't open $file: $!");
517 print NEWULP "# Begin of automatic generation\n";
518 foreach $test (sort @tests) {
519 if (defined $ulps{$test} || defined $failures{$test}) {
520 ($fct) = ($test =~ /^(\w+)\s/);
521 if ($fct ne $last_fct) {
522 $last_fct = $fct;
523 print NEWULP "\n# $fct\n";
524 }
525 print NEWULP "Test \"$test\":\n";
526 foreach $type (sort keys %{$ulps{$test}}) {
527 print NEWULP "$type: ", &clean_up_number ($ulps{$test}{$type}), "\n";
528 }
529 foreach $type (sort keys %{$failures{$test}}) {
530 print NEWULP "$type: fail\n";
531 }
532 }
533 }
534 print NEWULP "\n# Maximal error of functions:\n";
535
536 foreach $fct (sort @functions) {
537 if (defined $ulps{$fct} || defined $failures{$fct}) {
538 print NEWULP "Function \"$fct\":\n";
539 foreach $type (sort keys %{$ulps{$fct}}) {
540 print NEWULP "$type: ", &clean_up_number ($ulps{$fct}{$type}), "\n";
541 }
542 foreach $type (sort keys %{$failures{$fct}}) {
543 print NEWULP "$type: fail\n";
544 }
545 print NEWULP "\n";
546 }
547 }
548 print NEWULP "# end of automatic generation\n";
549 close NEWULP;
550}
551
552sub get_ulps {
553 my ($test, $float) = @_;
554 return exists $ulps{$test}{$float} ? $ulps{$test}{$float} : "0";
555}
556
557sub get_failure {
558 my ($test, $float) = @_;
559 return exists $failures{$test}{$float} ? $failures{$test}{$float} : "0";
560}
561
562# Output the defines for a single test
563sub output_test {
564 my ($file, $test, $name) = @_;
565 my ($ldouble, $double, $float, $ildouble, $idouble, $ifloat);
566
567 if (exists $ulps{$test}) {
568 $ldouble = &get_ulps ($test, "ldouble");
569 $double = &get_ulps ($test, "double");
570 $float = &get_ulps ($test, "float");
571 $ildouble = &get_ulps ($test, "ildouble");
572 $idouble = &get_ulps ($test, "idouble");
573 $ifloat = &get_ulps ($test, "ifloat");
574 print $file "#define DELTA$name CHOOSE($ldouble, $double, $float, $ildouble, $idouble, $ifloat)\t/* $test */\n";
575 }
576 if (exists $failures{$test}) {
577 $ldouble = &get_failure ($test, "ldouble");
578 $double = &get_failure ($test, "double");
579 $float = &get_failure ($test, "float");
580 $ildouble = &get_failure ($test, "ildouble");
581 $idouble = &get_failure ($test, "idouble");
582 $ifloat = &get_failure ($test, "ifloat");
583 print $file "#define FAIL$name CHOOSE($ldouble, $double, $float $ildouble, $idouble, $ifloat)\t/* $test */\n";
584 }
585}
586
587# Print include file
588sub output_ulps {
fe559c5e 589 my ($file, $ulps_filename) = @_;
8847214f
UD
590 my ($i, $fct);
591
592 open ULP, ">$file" or die ("Can't open $file: $!");
593
fe559c5e
UD
594 print ULP "/* This file is automatically generated\n";
595 print ULP " from $ulps_filename with gen-libm-test.pl.\n";
8847214f
UD
596 print ULP " Don't change it - change instead the master files. */\n\n";
597
fe559c5e 598 print ULP "\n/* Maximal error of functions. */\n";
8847214f
UD
599 foreach $fct (@functions) {
600 output_test (\*ULP, $fct, $fct);
601 }
602
fe559c5e 603 print ULP "\n/* Error of single function calls. */\n";
8847214f
UD
604 for ($i = 0; $i < $count; $i++) {
605 output_test (\*ULP, $tests[$i], $i);
606 }
607 close ULP;
608}
This page took 0.086427 seconds and 5 git commands to generate.