File: nvtoa_subnormal.t

package info (click to toggle)
libmath-mpfr-perl 4.45-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,716 kB
  • sloc: perl: 1,508; ansic: 517; makefile: 9
file content (129 lines) | stat: -rwxr-xr-x 3,145 bytes parent folder | download | duplicates (4)
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

# More checks on nvtoa's handling of subnormal values

use strict;
use warnings;
use Math::MPFR qw(:mpfr);
use Config;
use Test::More;

if(196869 < MPFR_VERSION) {

  my($bits, $pow, $str);

  if($Config{nvsize} == 8) {
    # nvtype is either double or 8-byte long double
    $bits = 53;
    $pow  = -1074;
  }
  elsif($Config{nvtype} eq '__float128') {
    $bits = 113;
    $pow  = -16494;
  }
  else {
    $bits = Math::MPFR::_required_ldbl_mant_dig();
    if($bits == 2098) {
      # nvtype is IBM DoubleDouble
      $bits = 53;
      $pow = -1074;
    }
    elsif($bits == 64 ) { $pow = -16445 }
    elsif($bits == 113) { $pow = -16494 }
    else                { die "Unknown nvtype" }
  }

  # Note that 2 ** $pow is the smallest positive (non-zero) value
  # that can be represented by the particular nvtype.

  if(2 ** $pow != 0) {
    $pow--;
    $bits++;

    my($val, $last_val) = (0, 0);

    for my $b(1 .. $bits) {
       next unless $b % 10; # Set every 10th to zero - just for some additional complexity
       $val = $last_val + (2 ** ($b + $pow));

       $str = nvtoa($val);
       cmp_ok(atonv($str), '==', $val,     "$b: atonv($str) == $val");
       cmp_ok(atonv($str), '>', $last_val, "$b: atonv($str) > $last_val");
       cmp_ok($str, 'eq', doubletoa($val), "$b: $str eq doubletoa($val)")
         if $Config{nvsize} == 8;

       other_checks($str, $val, $b);

       $last_val = $val;
    }
  }
  else {
    warn "\nSkipping all tests - this perl thinks that 2 ** $pow == 0\n";
    ok(1, "This perl is garbage"); # provide a test
  }

  done_testing();
}
else {

  eval {atonv('1.3');};
  like( $@, qr/^The atonv function requires mpfr\-3\.1\.6 or later/, '$@ reports that atonv is unavailable');

  done_testing();

}

sub other_checks {
  # check that $str is the shortest accurate representation of $val
  my($str, $val, $b) = (shift, shift, shift);
  my($newstr1, $newstr2, $skip) = ('', '', 0);

  # Replace the final digit of the significand with '0' and check
  # that the resultant value (in $newstr1) is less than $val.
  # Also, having removed the final digit of the original significand,
  # increment that value by 1 ULP, and check the resultant value (in
  # $newstr2) is greater than $val.

  # For this exercise all of the strings are of the
  # form significand . "e" . exponent.

  my @s = split /e/i, $str;
  $newstr1 = $s[0];
  chop $newstr1;
  $newstr2 = $newstr1;
  $skip = 1 unless length($newstr2);

  $newstr1 .= '0' . 'e' . $s[1];
  $newstr2 = plus_one_ulp($newstr2);
  $newstr2 .= 'e' . $s[1];

  cmp_ok(atonv($newstr1), '<', $val, "$b: atonv($newstr1) < $val");
  cmp_ok(atonv($newstr2), '>', $val, "$b: atonv($newstr2) > $val")
    unless $skip;

}


sub plus_one_ulp {
  my($ret, $pos) = (shift, 0);
  my $len = length($ret);

  for(1 .. $len) {
    if(substr($ret, -$_, 1) eq '.') {
      $pos = -$_;
      substr($ret, $pos, 1, '');
      last;
    }
  }

  $ret++;

  # Don't insert a '.' if the string that was given to this sub:
  # a) did not include a '.';
  #   OR
  # b) terminated with a '.'.

  substr($ret, length($ret) + $pos + 1, 0, '.')
    if $pos < -1;

  return $ret;
}