Package park :: Package util :: Module formatnum

Source Code for Module park.util.formatnum

  1  # This program is public domain 
  2  """ 
  3  Format numbers nicely for printing. 
  4   
  5  format_uncertainty_pm uses value +/- uncertainty 
  6   
  7  format_uncertainty_compact uses value(uncertainty) 
  8   
  9  format_uncertainty uses the default style, which is format_uncertainty_compact. 
 10  """ 
 11   
 12  # These routines need work for +/- formatting:: 
 13  # - pm formats are not rigorously tested 
 14  # - pm formats do not try to align the scale to multiples of 1000 
 15  # - pm formats do not try to align the value scale to uncertainty scale 
 16   
 17  # Coordinating scales across a set of numbers is not supported.  For easy 
 18  # comparison a set of numbers should be shown in the same scale.  One could 
 19  # force this from the outside by adding scale parameter (either 10**n, n, or 
 20  # a string representing the desired SI prefix) and having a separate routine 
 21  # which computes the scale given a set of values. 
 22   
 23  # Coordinating scales with units offers its own problems.  Again, the user 
 24  # may want to force particular units.  This can be done by outside of the 
 25  # formatting routines by scaling the numbers to the appropriate units then 
 26  # forcing them to print with scale 10**0.  If this is a common operation, 
 27  # however, it may want to happen inside. 
 28   
 29  # The value e<n> is currently formatted into the number.  Alternatively this 
 30  # scale factor could be returned so that the user can choose the appriate 
 31  # SI prefix when printing the units.  This gets tricky when talking about 
 32  # composite units such as 2.3e-3 m**2 -> 2300 mm**2, and with volumes 
 33  # such as 1 g/cm**3 -> 1 kg/L. 
 34   
 35  import math 
 36  import numpy 
 37  __all__ = ['format_uncertainty', 'format_uncertainty_pm', 
 38             'format_uncertainty_compact'] 
 39   
40 -def format_uncertainty_pm(value, uncertainty):
41 """ 42 Given a value and an uncertainty, return a string representation. 43 44 The returned string uses only the number of digits warranted by 45 the uncertainty in the measurement. 46 """ 47 if numpy.isinf(value): 48 return "inf" if value > 0 else "-inf" 49 50 if numpy.isnan(value): 51 return "NaN" 52 53 # Uncertainty check must come after indefinite check since the %g 54 # format string doesn't handle indefinite numbers consistently 55 # across platforms. 56 if uncertainty is None or uncertainty<=0 or numpy.isnan(uncertainty): 57 return "%g"%value 58 if numpy.isinf(uncertainty): 59 return "%g +/- inf"%value 60 61 # Process sign 62 sign = "-" if value < 0 else "" 63 value = abs(value) 64 65 # Represent error as (##) at the end of the digit string 66 val_place = int(math.floor(math.log10(value))) 67 err_place = int(math.floor(math.log10(uncertainty))) 68 scale = 10**(err_place-1) 69 val_digits = val_place-err_place+2 70 71 return "%s%.*g +/- %.2g"%(sign,val_digits,value,uncertainty)
72
73 -def format_uncertainty_compact(value,uncertainty):
74 """ 75 Given a value and an uncertainty, return a concise string representation. 76 77 The returned string uses only the number of digits warranted by the 78 uncertainty in the measurement. 79 """ 80 if numpy.isinf(value): 81 return "inf" if value > 0 else "-inf" 82 83 if numpy.isnan(value): 84 return "NaN" 85 86 # Uncertainty check must come after indefinite check since the %g 87 # format string doesn't handle indefinite numbers consistently 88 # across platforms. 89 if uncertainty is None or uncertainty<=0 or numpy.isnan(uncertainty): 90 return "%g"%value 91 if numpy.isinf(uncertainty): 92 return "%g(inf)"%value 93 94 # Process sign 95 sign = "-" if value < 0 else "" 96 value = abs(value) 97 98 # Represent error as (##) at the end of the digit string 99 val_place = int(math.floor(math.log10(value))) 100 err_place = int(math.floor(math.log10(uncertainty))) 101 err_str = "(%2d)"%int(uncertainty/10.**(err_place-1)+0.5) 102 103 if err_place > val_place: 104 # Degenerate case: error bigger than value 105 # The mantissa is 0.#(##)e#, 0.0#(##)e# or 0.00#(##)e# 106 if err_place - val_place > 2: value = 0 107 val_place = int(math.floor((err_place+2)/3.))*3 108 digits_after_decimal = val_place - err_place + 1 109 val_str = "%.*f%s"%(digits_after_decimal,value/10.**val_place,err_str) 110 if val_place != 0: val_str += "e%d"%val_place 111 elif err_place == val_place: 112 # Degenerate case: error and value the same order of magnitude 113 # The value is ##(##)e#, #.#(##)e# or 0.##(##)e# 114 val_place = int(math.floor((err_place+1)/3.))*3 115 digits_after_decimal = val_place - err_place + 1 116 val_str = "%.*f%s"%(digits_after_decimal,value/10.**val_place,err_str) 117 if val_place != 0: val_str += "e%d"%val_place 118 elif err_place <= 1 and val_place >= -3: 119 # Normal case: nice numbers and errors 120 # The value is ###.###(##) 121 digits_after_decimal = abs(err_place-1) 122 val_str = "%.*f%s"%(digits_after_decimal,value,err_str) 123 else: 124 # Extreme cases: zeros before value or after error 125 # The value is ###.###(##)e#, ##.####(##)e# or #.#####(##)e# 126 total_digits = val_place - err_place + 2 127 val_place = int(math.floor(val_place/3.))*3 128 val_str = "%.*g%se%d"%(total_digits, 129 value/10.**val_place, 130 err_str,val_place) 131 132 return sign+val_str
133 134 135 format_uncertainty = format_uncertainty_compact 136
137 -def test():
138 # Oops... renamed function after writing tests 139 value_str = format_uncertainty 140 141 # val_place > err_place 142 assert value_str(1235670,766000) == "1.24(77)e6" 143 assert value_str(123567.,76600) == "124(77)e3" 144 assert value_str(12356.7,7660) == "12.4(77)e3" 145 assert value_str(1235.67,766) == "1.24(77)e3" 146 assert value_str(123.567,76.6) == "124(77)" 147 assert value_str(12.3567,7.66) == "12.4(77)" 148 assert value_str(1.23567,.766) == "1.24(77)" 149 assert value_str(.123567,.0766) == "0.124(77)" 150 assert value_str(.0123567,.00766) == "0.0124(77)" 151 assert value_str(.00123567,.000766) == "0.00124(77)" 152 assert value_str(.000123567,.0000766) == "124(77)e-6" 153 assert value_str(.0000123567,.00000766) == "12.4(77)e-6" 154 assert value_str(.00000123567,.000000766) == "1.24(77)e-6" 155 assert value_str(.000000123567,.0000000766) == "124(77)e-9" 156 assert value_str(.00000123567,.0000000766) == "1.236(77)e-6" 157 assert value_str(.0000123567,.0000000766) == "12.357(77)e-6" 158 assert value_str(.000123567,.0000000766) == "123.567(77)e-6" 159 assert value_str(.00123567,.000000766) == "0.00123567(77)" 160 assert value_str(.0123567,.00000766) == "0.0123567(77)" 161 assert value_str(.123567,.0000766) == "0.123567(77)" 162 assert value_str(1.23567,.000766) == "1.23567(77)" 163 assert value_str(12.3567,.00766) == "12.3567(77)" 164 assert value_str(123.567,.0764) == "123.567(76)" 165 assert value_str(1235.67,.764) == "1235.67(76)" 166 assert value_str(12356.7,7.64) == "12356.7(76)" 167 assert value_str(123567,76.4) == "123567(76)" 168 assert value_str(1235670,764) == "1.23567(76)e6" 169 assert value_str(12356700,764) == "12.3567(76)e6" 170 assert value_str(123567000,7640) == "123.567(76)e6" 171 assert value_str(1235670000,76400) == "1.23567(76)e9" 172 173 # val_place == err_place 174 assert value_str(123567,764000) == "0.12(76)e6" 175 assert value_str(12356.7,76400) == "12(76)e3" 176 assert value_str(1235.67,7640) == "1.2(76)e3" 177 assert value_str(123.567,764) == "0.12(76)e3" 178 assert value_str(12.3567,76.4) == "12(76)" 179 assert value_str(1.23567,7.64) == "1.2(76)" 180 assert value_str(.123567,.764) == "0.12(76)" 181 assert value_str(.0123567,.0764) == "12(76)e-3" 182 assert value_str(.00123567,.00764) == "1.2(76)e-3" 183 assert value_str(.000123567,.000764) == "0.12(76)e-3" 184 185 # val_place == err_place-1 186 assert value_str(123567,7640000) == "0.1(76)e6" 187 assert value_str(12356.7,764000) == "0.01(76)e6" 188 assert value_str(1235.67,76400) == "0.001(76)e6" 189 assert value_str(123.567,7640) == "0.1(76)e3" 190 assert value_str(12.3567,764) == "0.01(76)e3" 191 assert value_str(1.23567,76.4) == "0.001(76)e3" 192 assert value_str(.123567,7.64) == "0.1(76)" 193 assert value_str(.0123567,.764) == "0.01(76)" 194 assert value_str(.00123567,.0764) == "0.001(76)" 195 assert value_str(.000123567,.00764) == "0.1(76)e-3" 196 197 # val_place == err_place-2 198 assert value_str(12356700,7640000000) == "0.0(76)e9" 199 assert value_str(1235670,764000000) == "0.00(76)e9" 200 assert value_str(123567,76400000) == "0.000(76)e9" 201 assert value_str(12356,7640000) == "0.0(76)e6" 202 assert value_str(1235,764000) == "0.00(76)e6" 203 assert value_str(123,76400) == "0.000(76)e6" 204 assert value_str(12,7640) == "0.0(76)e3" 205 assert value_str(1,764) == "0.00(76)e3" 206 assert value_str(0.1,76.4) == "0.000(76)e3" 207 assert value_str(0.01,7.64) == "0.0(76)" 208 assert value_str(0.001,0.764) == "0.00(76)" 209 assert value_str(0.0001,0.0764) == "0.000(76)" 210 assert value_str(0.00001,0.00764) == "0.0(76)e-3" 211 212 # val_place == err_place-3 213 assert value_str(12356700,76400000000) == "0.000(76)e12" 214 assert value_str(1235670,7640000000) == "0.0(76)e9" 215 assert value_str(123567,764000000) == "0.00(76)e9" 216 assert value_str(12356,76400000) == "0.000(76)e9" 217 assert value_str(1235,7640000) == "0.0(76)e6" 218 assert value_str(123,764000) == "0.00(76)e6" 219 assert value_str(12,76400) == "0.000(76)e6" 220 assert value_str(1,7640) == "0.0(76)e3" 221 assert value_str(0.1,764) == "0.00(76)e3" 222 assert value_str(0.01,76.4) == "0.000(76)e3" 223 assert value_str(0.001,7.64) == "0.0(76)" 224 assert value_str(0.0001,0.764) == "0.00(76)" 225 assert value_str(0.00001,0.0764) == "0.000(76)" 226 assert value_str(0.000001,0.00764) == "0.0(76)e-3" 227 228 # negative values 229 assert value_str(-1235670,765000) == "-1.24(77)e6" 230 assert value_str(-1.23567,.765) == "-1.24(77)" 231 assert value_str(-.00000123567,.0000000765) == "-1.236(77)e-6" 232 assert value_str(-12356.7,7.64) == "-12356.7(76)" 233 assert value_str(-123.567,764) == "-0.12(76)e3" 234 assert value_str(-1235.67,76400) == "-0.001(76)e6" 235 assert value_str(-.000123567,.00764) == "-0.1(76)e-3" 236 assert value_str(-12356,7640000) == "-0.0(76)e6" 237 assert value_str(-12,76400) == "-0.000(76)e6" 238 assert value_str(-0.0001,0.764) == "-0.00(76)" 239 240 # marked values 241 assert value_str(-numpy.inf,None) == "-inf" 242 assert value_str(numpy.inf,None) == "inf" 243 assert value_str(numpy.NaN,None) == "NaN" 244 245 # plus/minus form 246 assert format_uncertainty_pm(-1.23567,0.765) == "-1.24 +/- 0.77" 247 assert format_uncertainty_compact(-1.23567,0.765) == "-1.24(77)" 248 assert format_uncertainty(-1.23567,0.765) == "-1.24(77)" 249 250 # bad uncertainty 251 assert format_uncertainty_pm(-1.23567,numpy.NaN) == "-1.23567" 252 assert format_uncertainty_pm(-1.23567,-numpy.inf) == "-1.23567" 253 assert format_uncertainty_pm(-1.23567,-0.1) == "-1.23567" 254 assert format_uncertainty_compact(-1.23567,numpy.NaN) == "-1.23567" 255 assert format_uncertainty_compact(-1.23567,-numpy.inf) == "-1.23567" 256 assert format_uncertainty_compact(-1.23567,-0.1) == "-1.23567" 257 258 # no uncertainty 259 assert format_uncertainty_pm(-1.23567,0) == "-1.23567" 260 assert format_uncertainty_compact(-1.23567,0) == "-1.23567" 261 assert format_uncertainty_pm(-1.23567,None) == "-1.23567" 262 assert format_uncertainty_compact(-1.23567,None) == "-1.23567" 263 264 # inf uncertainty 265 assert format_uncertainty_pm(-1.23567,numpy.inf) == "-1.23567 +/- inf" 266 assert format_uncertainty_compact(-1.23567,numpy.inf) == "-1.23567(inf)"
267 268 if __name__ == "__main__": test() 269