sigfig.pro
4.64 KB
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
;+
; NAME:
; SIGFIG
;
;
; PURPOSE:
; Accept a scalar numerical value or an array of numbers and
; return the numbers as strings with the specified number of
; significant figures.
;
; CALLING SEQUENCE:
; RESULT = SigFig(Number, Nfig [, /SCIENTIFIC, /PLUSSES, /NUMERICAL)
;
; INPUTS:
; Number - Scalar or array of numerical values (float, double, int)
; Nfig - Number of signficant figures desired in the output
;
; OUTPUTS:
; String representation of the input with the specified number
; of signficant figures.
;
; KEYWORD PARAMTERS:
; /SCIENTIFIC - return the numbers in scientific notation
; /PLUSSES - Include plus signs for positive numbers
; /NUMERICAL - Return numerical, rather than string, values
;
; EXAMPLE:
; IDL> print, sigfig(-0.0001234, 2)
; -0.00012
; IDL> print, sigfig(1.234, 1)
; 1.
; IDL> print, sigfig(1234, 1)
; 1000
; IDL> print, sigfig(-0.0001234, 2, /sci)
; -1.2e-4
; IDL> print, sigfig(1234, 2, /plus)
; +1200
; IDL> print, sigfig(1234, 2, /plus, /sci)
; +1.2e+3
;
; MODIFICATION HISTORY:
; Inspired long ago by Erik Rosolowsky's SIGFIG:
; http://www.cfa.harvard.edu/~erosolow/idl/lib/lib.html#SIGFIG
;
; This version written by JohnJohn Sept 29, 2005
;
; 24 Oct 2007 - If result is a single number, return scalar value
; instead of an 1-element array. Thanks Mike Liu.
; 2 Apr 2008 - Fixed 1-element array issue, but for real this time.
;-
;;; SF_STR - The way STRING() should behave by default
function sf_str, stringin, format=format
return, strcompress(string(stringin, format=format), /rem)
end
;;; SF_TRANS_DEC - TRANSlate the DECimal point in a number of order
;;; unity, round it, and translate back.
function sf_trans_dec, numin, nsigin, order_inc=order_inc
nel = n_elements(numin)
;;; Double precision can't handle more than 19 sig figs
nsig = nsigin < 19
;;; Gonna have to move the decimal nsig-1 places to the right before rounding
move = nsig-1
len = max(strlen(numin))
move = move < (len-1)
;;; Create a string with just the digits, no decimal
nodec = strmid(numin, 0, 1)+strmid(numin, 2, len)
;;; Move the decimal, so nsig digits are to the left of the new
;;; decimal position
num0 = strmid(nodec,0,1+move)+'.'+strmid(nodec,1+move,len)
;;; Round the new number
num1 = sf_str(round(double(num0),/l64))
len1 = strlen(num1)
;;; If the number increases an order of magnitude after rounding, set
;;; order_inc=1 so the calling routine knows to add one to the order
;;; of magnitude
order_inc = len1 gt nsig
;;; Move the decimal back and return to sender
num = strmid(num1, 0, 1)+'.'+strmid(num1, 1, nsig-1)
return, num
end
function sigfig, NumIn, Nfig $
, string_return=string_return $
, scientific=scientific $
, numerical=numerical $
, plusses=plusses
Num = double(NumIn)
Nel = n_elements(Num)
;;; Convert the input number to scientific notation
TestString = sf_str(abs(double(Num)), format='(e)')
Epos = strpos(TestString[0], 'e')
;;; Test sign of the order
Osign = intarr(Nel)+1
StrOsign = strmid(TestString, Epos+1, 1)
Wneg = where(strosign eq '-', Nneg)
if Nneg gt 0 then Osign[Wneg] = -1
;;; Test sign of numbers, form string of minus signs for negative vals
NegSign = strarr(Nel) + (keyword_set(plusses) ? '+' : '')
Negative = where(Num lt 0, Nneg)
if Nneg gt 0 then NegSign[Negative] = '-'
;;; What are the orders of magnitude of the values?
Order = fix(sf_str(strmid(TestString, Epos+2, 2)))
;;; Convert all values to order unity for rounding
NumUnit = strmid(TestString,0,epos)
;;; Use TRANS_DEC to round unit values
NumTrans = sf_trans_dec(NumUnit, Nfig, order_inc=Order_Inc)
Order = order + Osign*order_inc
Len = strlen(NumTrans[0])
;;; Exit early without looping for /NUMERICAL or /SCIENTIFIC
if keyword_set(numerical) then begin
NumRound = NegSign+NumTrans+'e'+StrOsign+sf_str(Order)
if n_elements(NumRound) eq 1 then return, double(NumRound[0]) else $
return, double(NumRound)
endif
if keyword_set(scientific) then begin
NumRound = NegSign+NumTrans+'e'+StrOsign+sf_str(Order)
if n_elements(NumRound) eq 1 then return, NumRound[0] else $
return, NumRound
endif
NumRound = strarr(Nel)
for i = 0, Nel-1 do begin
if Osign[i]*Order[i]+1 gt Nfig then Format = '(I40)' else begin
d = sf_str(fix(Nfig-(Osign[i]*Order[i])-1) > 0)
Format = '(F40.' + d + ')'
endelse
New = NumTrans[i] * 10d^(Osign[i] * Order[i])
NumRound[i] = NegSign[i]+sf_str(New, format=Format)
endfor
if n_elements(NumRound) eq 1 then return, NumRound[0]
return, NumRound
end