Home | History | Annotate | Download | only in perldriver
      1 #!/usr/local/bin/perl
      2 #  ********************************************************************
      3 #  * Copyright (C) 2016 and later: Unicode, Inc. and others.
      4 #  * License & terms of use: http://www.unicode.org/copyright.html#License
      5 #  ********************************************************************
      6 #  ********************************************************************
      7 #  * COPYRIGHT:
      8 #  * Copyright (c) 2002, International Business Machines Corporation and
      9 #  * others. All Rights Reserved.
     10 #  ********************************************************************
     11 
     12 my $PLUS_MINUS = "±";
     13 
     14 #|#---------------------------------------------------------------------
     15 #|# Format a confidence interval, as given by a Dataset.  Output is as
     16 #|# as follows:
     17 #|#   241.23 - 241.98 => 241.5 +/- 0.3
     18 #|#   241.2 - 243.8 => 242 +/- 1
     19 #|#   211.0 - 241.0 => 226 +/- 15 or? 230 +/- 20
     20 #|#   220.3 - 234.3 => 227 +/- 7
     21 #|#   220.3 - 300.3 => 260 +/- 40
     22 #|#   220.3 - 1000 => 610 +/- 390 or? 600 +/- 400
     23 #|#   0.022 - 0.024 => 0.023 +/- 0.001
     24 #|#   0.022 - 0.032 => 0.027 +/- 0.005
     25 #|#   0.022 - 1.000 => 0.5 +/- 0.5
     26 #|# In other words, take one significant digit of the error value and
     27 #|# display the mean to the same precision.
     28 #|sub formatDataset {
     29 #|    my $ds = shift;
     30 #|    my $lower = $ds->getMean() - $ds->getError();
     31 #|    my $upper = $ds->getMean() + $ds->getError();
     32 #|    my $scale = 0;
     33 #|    # Find how many initial digits are the same
     34 #|    while ($lower < 1 ||
     35 #|           int($lower) == int($upper)) {
     36 #|        $lower *= 10;
     37 #|        $upper *= 10;
     38 #|        $scale++;
     39 #|    }
     40 #|    while ($lower >= 10 &&
     41 #|           int($lower) == int($upper)) {
     42 #|        $lower /= 10;
     43 #|        $upper /= 10;
     44 #|        $scale--;
     45 #|    }
     46 #|}
     47 
     48 #---------------------------------------------------------------------
     49 # Format a number, optionally with a +/- delta, to n significant
     50 # digits.
     51 #
     52 # @param significant digit, a value >= 1
     53 # @param multiplier
     54 # @param time in seconds to be formatted
     55 # @optional delta in seconds
     56 #
     57 # @return string of the form "23" or "23 +/- 10".
     58 #
     59 sub formatNumber {
     60     my $sigdig = shift;
     61     my $mult = shift;
     62     my $a = shift;
     63     my $delta = shift; # may be undef
     64     
     65     my $result = formatSigDig($sigdig, $a*$mult);
     66     if (defined($delta)) {
     67         my $d = formatSigDig($sigdig, $delta*$mult);
     68         # restrict PRECISION of delta to that of main number
     69         if ($result =~ /\.(\d+)/) {
     70             # TODO make this work for values with all significant
     71             # digits to the left of the decimal, e.g., 1234000.
     72 
     73             # TODO the other thing wrong with this is that it
     74             # isn't rounding the $delta properly.  Have to put
     75             # this logic into formatSigDig().
     76             my $x = length($1);
     77             $d =~ s/\.(\d{$x})\d+/.$1/;
     78         }
     79         $result .= " $PLUS_MINUS " . $d;
     80     }
     81     $result;
     82 }
     83 
     84 #---------------------------------------------------------------------
     85 # Format a time, optionally with a +/- delta, to n significant
     86 # digits.
     87 #
     88 # @param significant digit, a value >= 1
     89 # @param time in seconds to be formatted
     90 # @optional delta in seconds
     91 #
     92 # @return string of the form "23 ms" or "23 +/- 10 ms".
     93 #
     94 sub formatSeconds {
     95     my $sigdig = shift;
     96     my $a = shift;
     97     my $delta = shift; # may be undef
     98 
     99     my @MULT = (1   , 1e3,  1e6,  1e9);
    100     my @SUFF = ('s' , 'ms', 'us', 'ns');
    101 
    102     # Determine our scale
    103     my $i = 0;
    104     #always do seconds if the following line is commented out
    105     ++$i while ($a*$MULT[$i] < 1 && $i < @MULT);
    106     
    107     formatNumber($sigdig, $MULT[$i], $a, $delta) . ' ' . $SUFF[$i];
    108 }
    109 
    110 #---------------------------------------------------------------------
    111 # Format a percentage, optionally with a +/- delta, to n significant
    112 # digits.
    113 #
    114 # @param significant digit, a value >= 1
    115 # @param value to be formatted, as a fraction, e.g. 0.5 for 50%
    116 # @optional delta, as a fraction
    117 #
    118 # @return string of the form "23 %" or "23 +/- 10 %".
    119 #
    120 sub formatPercent {
    121     my $sigdig = shift;
    122     my $a = shift;
    123     my $delta = shift; # may be undef
    124     
    125     formatNumber($sigdig, 100, $a, $delta) . '%';
    126 }
    127 
    128 #---------------------------------------------------------------------
    129 # Format a number to n significant digits without using exponential
    130 # notation.
    131 #
    132 # @param significant digit, a value >= 1
    133 # @param number to be formatted
    134 #
    135 # @return string of the form "1234" "12.34" or "0.001234".  If
    136 #         number was negative, prefixed by '-'.
    137 #
    138 sub formatSigDig {
    139     my $n = shift() - 1;
    140     my $a = shift;
    141 
    142     local $_ = sprintf("%.${n}e", $a);
    143     my $sign = (s/^-//) ? '-' : '';
    144 
    145     my $a_e;
    146     my $result;
    147     if (/^(\d)\.(\d+)e([-+]\d+)$/) {
    148         my ($d, $dn, $e) = ($1, $2, $3);
    149         $a_e = $e;
    150         $d .= $dn;
    151         $e++;
    152         $d .= '0' while ($e > length($d));
    153         while ($e < 1) {
    154             $e++;
    155             $d = '0' . $d;
    156         }
    157         if ($e == length($d)) {
    158             $result = $sign . $d;
    159         } else {
    160             $result = $sign . substr($d, 0, $e) . '.' . substr($d, $e);
    161         }
    162     } else {
    163         die "Can't parse $_";
    164     }
    165     $result;
    166 }
    167 
    168 1;
    169 
    170 #eof
    171