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