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