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
|
#!/usr/local/bin/perl
# ********************************************************************
# * Copyright (C) 2016 and later: Unicode, Inc. and others.
# * License & terms of use: http://www.unicode.org/copyright.html
# ********************************************************************
# ********************************************************************
# * COPYRIGHT:
# * Copyright (c) 2002, International Business Machines Corporation and
# * others. All Rights Reserved.
# ********************************************************************
my $PLUS_MINUS = "±";
#|#---------------------------------------------------------------------
#|# Format a confidence interval, as given by a Dataset. Output is as
#|# as follows:
#|# 241.23 - 241.98 => 241.5 +/- 0.3
#|# 241.2 - 243.8 => 242 +/- 1
#|# 211.0 - 241.0 => 226 +/- 15 or? 230 +/- 20
#|# 220.3 - 234.3 => 227 +/- 7
#|# 220.3 - 300.3 => 260 +/- 40
#|# 220.3 - 1000 => 610 +/- 390 or? 600 +/- 400
#|# 0.022 - 0.024 => 0.023 +/- 0.001
#|# 0.022 - 0.032 => 0.027 +/- 0.005
#|# 0.022 - 1.000 => 0.5 +/- 0.5
#|# In other words, take one significant digit of the error value and
#|# display the mean to the same precision.
#|sub formatDataset {
#| my $ds = shift;
#| my $lower = $ds->getMean() - $ds->getError();
#| my $upper = $ds->getMean() + $ds->getError();
#| my $scale = 0;
#| # Find how many initial digits are the same
#| while ($lower < 1 ||
#| int($lower) == int($upper)) {
#| $lower *= 10;
#| $upper *= 10;
#| $scale++;
#| }
#| while ($lower >= 10 &&
#| int($lower) == int($upper)) {
#| $lower /= 10;
#| $upper /= 10;
#| $scale--;
#| }
#|}
#---------------------------------------------------------------------
# Format a number, optionally with a +/- delta, to n significant
# digits.
#
# @param significant digit, a value >= 1
# @param multiplier
# @param time in seconds to be formatted
# @optional delta in seconds
#
# @return string of the form "23" or "23 +/- 10".
#
sub formatNumber {
my $sigdig = shift;
my $mult = shift;
my $a = shift;
my $delta = shift; # may be undef
my $result = formatSigDig($sigdig, $a*$mult);
if (defined($delta)) {
my $d = formatSigDig($sigdig, $delta*$mult);
# restrict PRECISION of delta to that of main number
if ($result =~ /\.(\d+)/) {
# TODO make this work for values with all significant
# digits to the left of the decimal, e.g., 1234000.
# TODO the other thing wrong with this is that it
# isn't rounding the $delta properly. Have to put
# this logic into formatSigDig().
my $x = length($1);
$d =~ s/\.(\d{$x})\d+/.$1/;
}
$result .= " $PLUS_MINUS " . $d;
}
$result;
}
#---------------------------------------------------------------------
# Format a time, optionally with a +/- delta, to n significant
# digits.
#
# @param significant digit, a value >= 1
# @param time in seconds to be formatted
# @optional delta in seconds
#
# @return string of the form "23 ms" or "23 +/- 10 ms".
#
sub formatSeconds {
my $sigdig = shift;
my $a = shift;
my $delta = shift; # may be undef
my @MULT = (1 , 1e3, 1e6, 1e9);
my @SUFF = ('s' , 'ms', 'us', 'ns');
# Determine our scale
my $i = 0;
#always do seconds if the following line is commented out
++$i while ($a*$MULT[$i] < 1 && $i < @MULT);
formatNumber($sigdig, $MULT[$i], $a, $delta) . ' ' . $SUFF[$i];
}
#---------------------------------------------------------------------
# Format a percentage, optionally with a +/- delta, to n significant
# digits.
#
# @param significant digit, a value >= 1
# @param value to be formatted, as a fraction, e.g. 0.5 for 50%
# @optional delta, as a fraction
#
# @return string of the form "23 %" or "23 +/- 10 %".
#
sub formatPercent {
my $sigdig = shift;
my $a = shift;
my $delta = shift; # may be undef
formatNumber($sigdig, 100, $a, $delta) . '%';
}
#---------------------------------------------------------------------
# Format a number to n significant digits without using exponential
# notation.
#
# @param significant digit, a value >= 1
# @param number to be formatted
#
# @return string of the form "1234" "12.34" or "0.001234". If
# number was negative, prefixed by '-'.
#
sub formatSigDig {
my $n = shift() - 1;
my $a = shift;
local $_ = sprintf("%.${n}e", $a);
my $sign = (s/^-//) ? '-' : '';
my $a_e;
my $result;
if (/^(\d)\.(\d+)e([-+]\d+)$/) {
my ($d, $dn, $e) = ($1, $2, $3);
$a_e = $e;
$d .= $dn;
$e++;
$d .= '0' while ($e > length($d));
while ($e < 1) {
$e++;
$d = '0' . $d;
}
if ($e == length($d)) {
$result = $sign . $d;
} else {
$result = $sign . substr($d, 0, $e) . '.' . substr($d, $e);
}
} else {
die "Can't parse $_";
}
$result;
}
1;
#eof
|