Update.
[glibc.git] / math / gen-libm-test.pl
blob7a8c518d3ebefd0a1acf491370eb1513dc3dc9b4
1 #!/usr/bin/perl -w
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.
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.
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.
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.
22 # This file needs to be tidied up
23 # Note that functions and tests share the same namespace.
25 use Getopt::Std;
27 use strict;
29 use vars qw ($input $output);
30 use vars qw (@tests @functions);
31 use vars qw ($count);
32 use vars qw (%ulps %failures);
33 use vars qw (%beautify);
34 use vars qw ($output_dir $ulps_file);
36 %beautify =
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"
69 # get Options
70 # Options:
71 # u: ulps-file
72 # h: help
73 # o: output-directory
74 # n: generate new ulps file
75 use vars qw($opt_u $opt_h $opt_o $opt_n);
76 getopts('u:o:nh');
78 $ulps_file = 'libm-test-ulps';
79 $output_dir = '';
81 if ($opt_h) {
82 print "Usage: gen-libm-test.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;
90 $ulps_file = $opt_u if ($opt_u);
91 $output_dir = $opt_o if ($opt_o);
93 $input = "libm-test.inc";
94 $output = "${output_dir}libm-test.c";
96 $count = 0;
98 &parse_ulps ($ulps_file);
99 &generate_testfile ($input, $output);
100 &output_ulps ("${output_dir}libm-test-ulps.h", $ulps_file);
101 &print_ulps_file ("${output_dir}NewUlps") if ($opt_n);
103 # Return a nicer representation
104 sub beautify {
105 my ($arg) = @_;
106 my ($tmp);
108 if (exists $beautify{$arg}) {
109 return $beautify{$arg};
111 if ($arg =~ /^-/) {
112 $tmp = $arg;
113 $tmp =~ s/^-//;
114 if (exists $beautify{$tmp}) {
115 return '-' . $beautify{$tmp};
118 if ($arg =~ /[0-9]L$/) {
119 $arg =~ s/L$//;
121 return $arg;
124 # Return a nicer representation of a complex number
125 sub build_complex_beautify {
126 my ($r, $i) = @_;
127 my ($str1, $str2);
129 $str1 = &beautify ($r);
130 $str2 = &beautify ($i);
131 if ($str2 =~ /^-/) {
132 $str2 =~ s/^-//;
133 $str1 .= ' - ' . $str2;
134 } else {
135 $str1 .= ' + ' . $str2;
137 $str1 .= ' i';
138 return $str1;
141 # Return name of a variable
142 sub get_variable {
143 my ($number) = @_;
145 return "x" if ($number == 1);
146 return "y" if ($number == 2);
147 return "z" if ($number == 3);
148 # return x1,x2,...
149 $number =-3;
150 return "x$number";
153 # Add a new test to internal data structures and fill in the
154 # ulps, failures and exception information for the C line.
155 sub new_test {
156 my ($test, $exception) = @_;
157 my $rest;
159 # Add ulp, xfail
160 if (exists $ulps{$test}) {
161 $rest = ", DELTA$count";
162 } else {
163 $rest = ', 0';
165 if (exists $failures{$test}) {
166 $rest .= ", FAIL$count";
167 } else {
168 $rest .= ', 0';
170 if (defined $exception) {
171 $rest .= ", $exception";
172 } else {
173 $rest .= ', 0';
175 $rest .= ");\n";
176 # We must increment here to keep @tests and count in sync
177 push @tests, $test;
178 ++$count;
179 return $rest;
182 # Treat some functions especially.
183 # Currently only sincos needs extra treatment.
184 sub special_functions {
185 my ($file, $args) = @_;
186 my (@args, $str, $test, $cline);
188 @args = split /,\s*/, $args;
190 unless ($args[0] =~ /sincos/) {
191 die ("Don't know how to handle $args[0] extra.");
193 print $file " FUNC (sincos) ($args[1], &sin_res, &cos_res);\n";
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]);
202 $cline = " check_float (\"$test\", sin_res, $args[2]";
203 $cline .= &new_test ($test, $args[4]);
204 print $file $cline;
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;
214 # Parse the arguments to TEST_x_y
215 sub 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);
222 if ($descr eq 'extra') {
223 &special_functions ($file, $args);
224 return;
226 ($descr_args, $descr_res) = split /_/,$descr, 2;
228 @args = split /,\s*/, $args;
230 $call = "$args[0] (";
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 .= ', ';
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;
246 # &FLOAT, &int - argument is added here
247 if ($descr[$i] =~ /F|I/) {
248 ++$extra_var;
249 $call .= '&' . &get_variable ($extra_var);
250 next;
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;
259 die ("$descr[$i] is unknown");
261 $call .= ')';
262 $str = "$call == ";
264 # Result
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");
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");
294 # check for exceptions
295 if ($current_arg <= $#args) {
296 $str .= " plus " . &beautify ($args[$current_arg]);
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';
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]) (";
323 @descr = split //,$descr_args;
324 for ($i=0; $i <= $#descr; $i++) {
325 if ($i >= 1) {
326 $c_call .= ', ';
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;
334 # &FLOAT, &int
335 if ($descr[$i] =~ /F|I/) {
336 ++$extra_var;
337 $c_call .= '&' . &get_variable ($extra_var);
338 next;
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;
347 $c_call .= ')';
348 $cline .= "$c_call, ";
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;
363 # Add ulp, xfail
364 $cline .= &new_test ($str, ($current_arg <= $#args) ? $args[$current_arg] : undef);
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);
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);
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);
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);
394 print $file $pre if (defined $pre);
396 print $file " $cline\n";
398 print $file $post if (defined $post);
401 # Generate libm-test.c
402 sub generate_testfile {
403 my ($input, $output) = @_;
404 my ($lasttext);
405 my (@args, $i, $str);
407 open INPUT, $input or die ("Can't open $input: $!");
408 open OUTPUT, ">$output" or die ("Can't open $output: $!");
410 # Replace the special macros
411 while (<INPUT>) {
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;
421 # START (function)
422 if (/START/) {
423 print OUTPUT " init_max_error ();\n";
424 next;
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';
436 if (exists $failures{$fct}) {
437 $line .= ", FAIL$fct";
438 } else {
439 $line .= ', 0';
441 $line .= ");\n";
442 print OUTPUT $line;
443 push @functions, $fct;
444 next;
446 print OUTPUT;
448 close INPUT;
449 close OUTPUT;
454 # Parse ulps file
455 sub parse_ulps {
456 my ($file) = @_;
457 my ($test, $type, $eps);
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;
470 if (/^Function/) {
471 ($test) = ($_ =~ /^Function\s*\"([a-zA-Z0-9_]+)\"/);
472 next;
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;
481 next;
483 print "Skipping unknown entry: `$_'\n";
485 close ULP;
488 # Just for testing: Print all ulps
489 sub print_ulps {
490 my ($test, $type, $eps);
492 foreach $test (keys %ulps) {
493 print "$test:\n";
494 foreach $type (keys %{$ulps{$test}}) {
495 print "$test: $type $ulps{$test}{$type}\n";
500 # Clean up a floating point number
501 sub clean_up_number {
502 my ($number) = @_;
504 # Remove trailing zeros
505 $number =~ s/0+$//;
506 $number =~ s/\.$//;
507 return $number;
510 # Output a file which can be read in as ulps file.
511 sub print_ulps_file {
512 my ($file) = @_;
513 my ($test, $type, $eps, $fct, $last_fct);
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";
525 print NEWULP "Test \"$test\":\n";
526 foreach $type (sort keys %{$ulps{$test}}) {
527 print NEWULP "$type: ", &clean_up_number ($ulps{$test}{$type}), "\n";
529 foreach $type (sort keys %{$failures{$test}}) {
530 print NEWULP "$type: fail\n";
534 print NEWULP "\n# Maximal error of functions:\n";
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";
542 foreach $type (sort keys %{$failures{$fct}}) {
543 print NEWULP "$type: fail\n";
545 print NEWULP "\n";
548 print NEWULP "# end of automatic generation\n";
549 close NEWULP;
552 sub get_ulps {
553 my ($test, $float) = @_;
554 return exists $ulps{$test}{$float} ? $ulps{$test}{$float} : "0";
557 sub get_failure {
558 my ($test, $float) = @_;
559 return exists $failures{$test}{$float} ? $failures{$test}{$float} : "0";
562 # Output the defines for a single test
563 sub output_test {
564 my ($file, $test, $name) = @_;
565 my ($ldouble, $double, $float, $ildouble, $idouble, $ifloat);
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";
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";
587 # Print include file
588 sub output_ulps {
589 my ($file, $ulps_filename) = @_;
590 my ($i, $fct);
592 open ULP, ">$file" or die ("Can't open $file: $!");
594 print ULP "/* This file is automatically generated\n";
595 print ULP " from $ulps_filename with gen-libm-test.pl.\n";
596 print ULP " Don't change it - change instead the master files. */\n\n";
598 print ULP "\n/* Maximal error of functions. */\n";
599 foreach $fct (@functions) {
600 output_test (\*ULP, $fct, $fct);
603 print ULP "\n/* Error of single function calls. */\n";
604 for ($i = 0; $i < $count; $i++) {
605 output_test (\*ULP, $tests[$i], $i);
607 close ULP;