1# -*- coding: utf-8 -*- 2 3from math import log10, floor 4 5from ..units import html_of_unit, latex_of_unit, unicode_of_unit, to_unitless, unit_of 6from ..util.parsing import _unicode_sup 7 8 9def roman(num): 10 """ 11 Examples 12 -------- 13 >>> roman(4) 14 'IV' 15 >>> roman(17) 16 'XVII' 17 18 """ 19 tokens = "M CM D CD C XC L XL X IX V IV I".split() 20 values = 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 21 result = "" 22 for t, v in zip(tokens, values): 23 cnt = num // v 24 result += t * cnt 25 num -= v * cnt 26 return result 27 28 29def _mag(num): 30 return int(floor(log10(abs(num)))) 31 32 33def _float_str_w_uncert(x, xe, precision=2): 34 """Prints uncertain number with parenthesis 35 36 Parameters 37 ---------- 38 x : nominal value 39 xe : uncertainty 40 precision : number of significant digits in uncertainty 41 42 Examples 43 -------- 44 >>> _float_str_w_uncert(-9.99752e5, 349, 3) 45 '-999752(349)' 46 >>> _float_str_w_uncert(-9.99752e15, 349e10, 2) 47 '-9.9975(35)e15' 48 >>> _float_str_w_uncert(3.1416, 0.029, 1) 49 '3.14(3)' 50 >>> _float_str_w_uncert(3.1416e9, 2.9e6, 1) 51 '3.142(3)e9' 52 53 Returns 54 ------- 55 shortest string representation of "x +- xe" either as 56 ``x.xx(ee)e+xx`` or ``xxx.xx(ee)`` 57 58 Notes 59 ----- 60 The code in this function is from a question on StackOverflow: 61 http://stackoverflow.com/questions/6671053 62 written by: 63 Lemming, http://stackoverflow.com/users/841562/lemming 64 the code is licensed under 'CC-WIKI'. 65 (see: http://blog.stackoverflow.com/2009/06/attribution-required/) 66 67 """ 68 # base 10 exponents 69 x_exp = int(floor(log10(abs(x)))) 70 xe_exp = int(floor(log10(abs(xe)))) 71 72 # uncertainty 73 un_exp = xe_exp - precision + 1 74 un_int = round(xe * 10 ** (-un_exp)) 75 76 # nominal value 77 no_exp = un_exp 78 no_int = round(x * 10 ** (-no_exp)) 79 80 # format - nom(unc)exp 81 fieldw = x_exp - no_exp 82 fmt = "%%.%df" % fieldw 83 result1 = (fmt + "(%.0f)e%d") % (no_int * 10 ** (-fieldw), un_int, x_exp) 84 85 # format - nom(unc) 86 fieldw = max(0, -no_exp) 87 fmt = "%%.%df" % fieldw 88 result2 = (fmt + "(%.0f)") % (no_int * 10 ** no_exp, un_int * 10 ** max(0, un_exp)) 89 90 # return shortest representation 91 if len(result2) <= len(result1): 92 return result2 93 else: 94 return result1 95 96 97def _number_to_X(number, uncertainty, unit, fmt, unit_fmt, fmt_pow_10, space=" "): 98 uncertainty = uncertainty or getattr(number, "uncertainty", None) 99 unit = unit or unit_of(number) 100 integer_one = 1 101 if unit is integer_one: 102 unit_str = "" 103 mag = number 104 else: 105 unit_str = space + unit_fmt(unit) 106 mag = to_unitless(number, unit) 107 if uncertainty is not None: 108 uncertainty = to_unitless(uncertainty, unit) 109 110 if uncertainty is None: 111 if fmt is None: 112 fmt = 5 113 if isinstance(fmt, int): 114 flt = ("%%.%dg" % fmt) % mag 115 else: 116 flt = fmt(mag) 117 else: 118 if fmt is None: 119 fmt = 2 120 if isinstance(fmt, int): 121 flt = _float_str_w_uncert(mag, uncertainty, fmt) 122 else: 123 flt = fmt(mag, uncertainty) 124 if "e" in flt: 125 significand, mantissa = flt.split("e") 126 return fmt_pow_10(significand, mantissa) + unit_str 127 else: 128 return flt + unit_str 129 130 131def _latex_pow_10(significand, mantissa): 132 if significand in ("1", "1.0"): 133 fmt = "10^{%s}" 134 else: 135 fmt = significand + r"\cdot 10^{%s}" 136 return fmt % str(int(mantissa)) 137 138 139def number_to_scientific_latex(number, uncertainty=None, unit=None, fmt=None): 140 r"""Formats a number as LaTeX (optionally with unit/uncertainty) 141 142 Parameters 143 ---------- 144 number : float (w or w/o unit) 145 uncertainty : same as number 146 unit : unit 147 fmt : int or callable 148 149 Examples 150 -------- 151 >>> number_to_scientific_latex(3.14) == '3.14' 152 True 153 >>> number_to_scientific_latex(3.14159265e-7) 154 '3.1416\\cdot 10^{-7}' 155 >>> import quantities as pq 156 >>> number_to_scientific_latex(2**0.5 * pq.m / pq.s) 157 '1.4142\\,\\mathrm{\\frac{m}{s}}' 158 >>> number_to_scientific_latex(1.23456, .789, fmt=2) 159 '1.23(79)' 160 161 """ 162 return _number_to_X( 163 number, uncertainty, unit, fmt, latex_of_unit, _latex_pow_10, r"\," 164 ) 165 166 167def _unicode_pow_10(significand, mantissa): 168 if significand in ("1", "1.0"): 169 result = u"10" 170 else: 171 result = significand + u"·10" 172 return result + u"".join(map(_unicode_sup.get, str(int(mantissa)))) 173 174 175def number_to_scientific_unicode(number, uncertainty=None, unit=None, fmt=None): 176 u"""Formats a number as unicode (optionally with unit/uncertainty) 177 178 Parameters 179 ---------- 180 number : float (w or w/o unit) 181 uncertainty : same as number 182 unit : unit 183 fmt : int or callable 184 185 Examples 186 -------- 187 >>> number_to_scientific_unicode(3.14) == u'3.14' 188 True 189 >>> number_to_scientific_unicode(3.14159265e-7) == u'3.1416·10⁻⁷' 190 True 191 >>> import quantities as pq 192 >>> number_to_scientific_unicode(2**0.5 * pq.m / pq.s) 193 '1.4142 m/s' 194 195 """ 196 return _number_to_X( 197 number, uncertainty, unit, fmt, unicode_of_unit, _unicode_pow_10 198 ) 199 200 201def _html_pow_10(significand, mantissa): 202 if significand in ("1", "1.0"): 203 result = "10<sup>" 204 else: 205 result = significand + "⋅10<sup>" 206 return result + str(int(mantissa)) + "</sup>" 207 208 209def number_to_scientific_html(number, uncertainty=None, unit=None, fmt=None): 210 r"""Formats a number as HTML (optionally with unit/uncertainty) 211 212 Parameters 213 ---------- 214 number : float (w or w/o unit) 215 uncertainty : same as number 216 unit : unit 217 fmt : int or callable 218 219 Examples 220 -------- 221 >>> number_to_scientific_html(3.14) == '3.14' 222 True 223 >>> number_to_scientific_html(3.14159265e-7) 224 '3.1416⋅10<sup>-7</sup>' 225 >>> number_to_scientific_html(1e13) 226 '10<sup>13</sup>' 227 >>> import quantities as pq 228 >>> number_to_scientific_html(2**0.5 * pq.m / pq.s) 229 '1.4142 m/s' 230 231 """ 232 return _number_to_X(number, uncertainty, unit, fmt, html_of_unit, _html_pow_10) 233