source: trunk/Include/Classes/ActiveBasic/Strings/SPrintF.ab@ 364

Last change on this file since 364 was 364, checked in by イグトランス (egtra), 17 years ago

FormatFloatFを実装

File size: 14.1 KB
Line 
1'Classes/ActiveBasic/Strings/SPrintF.ab
2
3Namespace ActiveBasic
4Namespace Strings
5
6Namespace Detail
7
8/*!
9@brief 浮動小数点数を文字列化する低水準な関数。符号、指数、仮数に分けて出力。
10@author Egtra
11@date 2007/09/18
12@param[in] x 文字列化する浮動小数点数
13@param[out] e 指数
14@param[out] sign 符号
15@return 仮数
16仮数は1の位から下へ17桁で、小数点を含まない。そのため、誤差を無視すればVal(仮数) * 10 ^ (e - 17) = Abs(x)が成り立つ。
17
18xに無限大、非数を渡した場合の動作は未定義。
19*/
20Function FloatToChars(x As Double, ByRef e As Long, ByRef sign As Boolean) As String
21 Imports System
22
23 '0を弾く
24 If x = 0 Then
25 If GetQWord(VarPtr(x) As *QWord) And &h8000000000000000 Then
26 sign = True
27 Else
28 sign = False
29 End If
30
31 e = 0
32 FloatToChars = "00000000000000000"
33 Exit Function
34 End If
35
36 '符号の判断(同時に符号を取り除く)
37 If x < 0 Then
38 sign = True
39 x = -x
40 Else
41
42 sign = False
43 End If
44
45 '1e16 <= x < 1e17へ正規化
46 '(元のx) = (正規化後のx) ^ (d - 17)である。
47 Dim d = Math.Floor(Math.Log10(x)) As Long
48 If d < 16 Then
49 x *= ipow(10, +17 - d)
50 ElseIf d > 16 Then
51 x /= ipow(10, -17 + d)
52 End If
53
54 '補正
55 While x < 1e16
56 x *= 10
57 d--
58 Wend
59 While x >= 1e17
60 x /= 10
61 d++
62 Wend
63
64 d--
65 e = d
66
67 FloatToChars = Str$(x As QWord)
68End Function
69
70/*!
71@brief 書式化関数群で使用するフラグ。
72@author Egtra
73@date 2007/09/18
74*/
75Const Enum FormatFlags
76 '! 何も指定がない。
77 None = &h0
78 /*!
79 符号、+。符号付変換[diAaEeFfGg]のとき、正の値でも符号を付ける。
80 AdjustFieldWidthの仕様から、Format関数郡内からAdjustFieldWidthにかけて、
81 単に数値が符号付である(負の値である)ことを示す意味でも用いられる。
82 */
83 Sign = &h1
84 /*! 空白、空白文字。
85 符号付変換[diAaEeFfGg]のとき、正の値ならば符号分の空白を開ける。Signが立っているときには無視される。
86 */
87 Blank = &h2
88 /*! ゼロ、0。
89 [diouXxAaEeFfGg]で、フィールドの空きを0で埋める。leftが立っているときには無視される。
90 */
91 Zero = &h4
92 '! 左揃え、-。フィールド内で左揃えにする。
93 Left = &h8
94 /*! 代替表記、#。
95 <ul>
96 <li>[OoXx]では、値が0でない場合、先頭に0、0xを付ける。</ul>
97 <li>[AaEeFfGg]では、精度0でも小数点を付ける。</ul>
98 <li>[Gg]では、それに加え、小数部末尾の0の省略を行わないようにする。</ul>
99 </ul>
100 */
101 Alt = &h10
102 '! 大文字。使用するアルファベットを大文字にする。[aefgx]を[AEFGX]化する。
103 Cap = &h20
104End Enum
105
106/*!
107@brief 浮動小数点数をprintfの%e, %E(指数形式、十進法)相当の変換で文字列化する関数。
108@author Egtra
109@date 2007/09/18
110@param[in] x 文字列化する浮動小数点数値。
111@param[in] d 精度。小数点以下の桁数。DWORD_MAXのとき、指定なしとして既定値6となる。
112@param[in] field フィールド幅。
113@param[in] flags 書式フラグ。
114@return xの文字列表現
115
116@todo 他の実装での末尾桁の扱いを調べる(このコードでは何もしていないので切捨となっている)。
117*/
118Function FormatFloatE(x As Double, d As DWord, field As DWord, flags As FormatFlags) As String
119 If d = DWORD_MAX Then
120 d = 6
121 End If
122
123 Dim e As Long, negative As Boolean
124 Dim s = FloatToChars(x, e, negative)
125
126 Dim sb = New System.Text.StringBuilder
127 With sb
128
129 AppendSign(sb, negative, flags)
130
131 .Append(s[0])
132
133 If (flags And Alt) Or d > 0 Then
134 .Append(".")
135 Dim outputLen = s.Length - 1
136 If outputLen >= d Then
137 .Append(s, 1, d)
138 Else 'sで用意された桁が指定された精度より少ないとき
139 .Append(s, 1, outputLen)
140 .Append(&h30 As StrChar, d - outputLen) '足りない桁は0埋め
141 End If
142 End If
143
144 If flags And Cap Then
145 .Append("E")
146 Else
147 .Append("e")
148 End If
149
150 .Append(FormatIntegerD(e, 2, 0, Sign Or Zero))
151
152 AdjustFieldWidth(sb, field, flags)
153 End With
154 FormatFloatE = sb.ToString()
155End Function
156
157/*!
158@brief 浮動小数点数をprintfの%f(小数点数形式、十進法)相当の変換で文字列化する関数。
159@author Egtra
160@date 2007/10/23
161@param[in] x 文字列化する浮動小数点数値。
162@param[in] precision 精度。小数点以下の桁数。DWORD_MAXのとき、指定なしとして既定値6となる。
163@param[in] field フィールド幅。
164@param[in] flags 書式フラグ。
165@return xの文字列表現
166*/
167Function FormatFloatF(x As Double, precision As DWord, field As DWord, flags As FormatFlags) As String
168 If precision = DWORD_MAX Then
169 precision = 6
170 End If
171
172 Dim e As Long, negative As Boolean
173 Dim s = FloatToChars(x, e, negative)
174
175 Dim sb = New System.Text.StringBuilder
176 With sb
177 AppendSign(sb, negative, flags)
178
179 Dim intPartLen = e + 1
180 Dim outputDigit = 0 As DWord
181 If intPartLen >= 17 Then
182 '有効桁が全て整数部に収まる場合
183 .Append(s)
184 .Append(&h30 As StrChar, intPartLen - 17)
185 outputDigit = 17
186 ElseIf intPartLen > 0 Then
187 '有効桁の一部が整数部にかかる場合
188 .Append(s, 0, intPartLen)
189 outputDigit = intPartLen
190 Else
191 '有効桁が全く整数部にかからない場合
192 .Append(&h30 As StrChar)
193 End If
194
195 If precision > 0 Or (flags And Alt) Then
196 .Append(".")
197
198 Dim lastDigit = s.Length - outputDigit
199 If lastDigit >= precision Then '変換して得られた文字列の桁数が精度以上ある場合
200 Dim zeroDigit = 0
201 If intPartLen < 0 Then
202 '1.23e-4 = 0.000123のように指数が負のため小数点以下に0が続く場合
203 zeroDigit = System.Math.Min(-intPartLen As DWord, precision)
204 .Append(&h30 As StrChar, zeroDigit As Long)
205 End If
206 .Append(s, outputDigit, (precision - zeroDigit) As Long)
207 Else
208 .Append(s, outputDigit, lastDigit)
209 .Append(&h30 As StrChar, (precision - lastDigit) As Long) '残りの桁は0埋め
210 End If
211 End If
212 AdjustFieldWidth(sb, field, flags)
213 End With
214 FormatFloatF = sb.ToString()
215End Function
216
217/*!
218@brief 先頭に符号もしくはその分の空白を出力する。FormatFloat用。
219@author Egtra
220@date 2007/10/23
221@param[in, out] sb 出力先
222@param[in] negative 符号
223@param[in, out] flags フラグ。negative = Trueなら、Signを立てて返す。
224*/
225Sub AppendSign(sb As System.Text.StringBuilder, negative As Boolean, ByRef flags As FormatFlags)
226 With sb
227 If negative Then
228 .Append("-")
229 flags Or= Sign
230 Else
231 If flags And Sign Then
232 .Append("+")
233 ElseIf flags And Blank Then
234 .Append(" ")
235 End If
236 End If
237 End With
238End Sub
239
240'! DWordの最大値4294967295の文字数 - 1。FormatIntegerU内で使用。
241Const MaxSizeU = 9
242
243/*!
244@brief 符号無し整数をprintfの%u(十進法表現)相当の変換で文字列化する関数。
245@author Egtra
246@date 2007/09/18
247@param[in] x 文字列化する整数値。
248@param[in] d 精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。
249@param[in] field フィールド幅。
250@param[in] flags 書式フラグ。
251@return xの文字列表現
252*/
253Function FormatIntegerU(x As DWord, d As DWord, field As DWord, flags As FormatFlags) As String
254 FormatIntegerU = FormatInteger(x, d, field, flags And (Not (Sign Or Blank)), 0)
255End Function
256
257/*!
258DWordの最大値4294967295やLongの最大値2147483647、最小値-2147483648
259(全て十進法)の符号部を除いた文字数 - 1。FormatIntegerU内で使用。
260*/
261Const MaxSizeD = 9
262
263/*!
264@brief 符号有り整数をprintfの%d(十進法表現)相当の変換で文字列化する関数。
265@author Egtra
266@date 2007/10/13
267@param[in] x 文字列化する整数値。
268@param[in] d 精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。
269@param[in] field フィールド幅。
270@param[in] flags 書式フラグ。
271@return xの文字列表現
272*/
273Function FormatIntegerD(x As Long, d As DWord, field As DWord, flags As FormatFlags) As String
274 Dim dwX As DWord
275
276 Dim signChar As StrChar
277 If x < 0 Then
278 dwX = (-x) As DWord
279 signChar = Asc("-")
280 flags Or= Sign
281 Else
282 dwX = x As DWord
283 If flags And Sign Then
284 signChar = Asc("+")
285 ElseIf flags And Blank Then
286 signChar = Asc(" ")
287 End If
288 End If
289
290 FormatIntegerD = FormatInteger(dwX, d, field, flags, signChar)
291End Function
292
293/*!
294@brief 整数をprintfの%d, %u相当の変換で文字列化する関数。
295@author Egtra
296@date 2007/09/18
297@param[in] x 文字列化する整数値。
298@param[in] d 精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。
299@param[in] field フィールド幅。
300@param[in] flags 書式フラグ。
301@param[in] signChar 符号部分の文字。\0なら存在しないとして扱われる。
302@return xの文字列表現
303
304signCharに何らかの値を指定するときには、必ずflagsにSignまたはBlankを指定すること。
305さもないと、フィールド幅を指定したときに正しく整形されなくなる。
306*/
307Function FormatInteger(x As DWord, d As DWord, field As DWord, flags As FormatFlags, signChar As StrChar) As String
308 PreProcessFormatInteger(d, flags)
309
310 Dim sb = New System.Text.StringBuilder
311 With sb
312 If signChar <> 0 Then
313 .Append(signChar)
314 End If
315
316 Dim buf[MaxSizeU] As StrChar
317 Dim i = MaxSizeU
318 While x <> 0
319 buf[i] = (x As Int64 Mod 10 + &h30) As StrChar 'Int64への型変換は#117対策
320 x \= 10
321 i--
322 Wend
323
324 Dim len = (MaxSizeU - i) As Long
325 If len < d Then
326 .Append(&h30 As StrChar, d - len)
327 End If
328
329 .Append(buf, i + 1, len)
330
331 AdjustFieldWidth(sb, field, flags)
332 End With
333 FormatInteger = sb.ToString()
334End Function
335
336/*!
337DWordの最大値の八進法表現37777777777の文字数 - 1 + 1。FormatIntegerO内で使用。
338上の式で1を加えているのは、八進接頭辞の分。
339*/
340Const MaxSizeO = 11
341
342Dim TraitsIntegerO As IntegerConvertTraits
343With TraitsIntegerO
344 .Convert = AddressOf(IntegerO_Convert)
345 .Prefix = AddressOf(IntegerO_Prefix)
346 .MaxSize = MaxSizeO
347End With
348
349/*!
350@brief 符号無し整数をprintfの%o(八進法表現)相当の変換で文字列化する関数。
351@author Egtra
352@date 2007/10/19
353@param[in] x 文字列化する整数値。
354@param[in] d 精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。
355@param[in] field フィールド幅。
356@param[in] flags 書式フラグ。
357@return xの文字列表現
358*/
359Function FormatIntegerO(x As DWord, d As DWord, field As DWord, flags As FormatFlags) As String
360 FormatIntegerO = FormatIntegerEx(TraitsIntegerO, x, d, field, flags)
361End Function
362
363Function IntegerO_Convert(buf As *StrChar, xq As QWord, flags As FormatFlags) As DWord
364 Dim x = xq As DWord
365 Dim i = MaxSizeO
366 While x <> 0
367 buf[i] = ((x And &o7) + &h30) As StrChar
368 x >>= 3
369 i--
370 Wend
371 If flags And Alt Then
372 buf[i] = &h30
373 i--
374 End If
375 IntegerO_Convert = i
376End Function
377
378Function IntegerO_Prefix(x As QWord, flags As FormatFlags) As String
379End Function
380
381/*!
382DWordの最大値の十六進法表現ffffffffの文字数 - 1。FormatIntegerO内で使用。
383*/
384Const MaxSizeX = 7
385
386Dim TraitsIntegerX As IntegerConvertTraits
387With TraitsIntegerX
388 .Convert = AddressOf(IntegerX_Convert)
389 .Prefix = AddressOf(IntegerX_Prefix)
390 .MaxSize = MaxSizeX
391End With
392
393/*!
394@brief 整数をprintfの%x, %X(十六進法)相当の変換で文字列化する関数。
395@author Egtra
396@date 2007/10/19
397@param[in] x 文字列化する整数値。
398@param[in] d 精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。
399@param[in] field フィールド幅。
400@param[in] flags 書式フラグ。
401@return xの文字列表現
402*/
403Function FormatIntegerX(x As DWord, d As DWord, field As DWord, flags As FormatFlags) As String
404 FormatIntegerX = FormatIntegerEx(TraitsIntegerX, x, d, field, flags)
405End Function
406
407Function IntegerX_Convert(buf As *StrChar, xq As QWord, flags As FormatFlags) As DWord
408 Dim i = MaxSizeX
409 Dim x = xq As DWord
410 While x <> 0
411 buf[i] = _System_HexadecimalTable[x And &h0f]
412 x >>= 4
413 i--
414 Wend
415 IntegerX_Convert = i
416End Function
417
418Function IntegerX_Prefix(x As QWord, flags As FormatFlags) As String
419 If flags And Alt Then
420 If x <> 0 Then
421 IntegerX_Prefix = "0X"
422 End If
423 End If
424End Function
425
426Type IntegerConvertTraits
427 Convert As *Function(buf As *StrChar, x As QWord, flags As FormatFlags) As DWord
428 Prefix As *Function(x As QWord, flags As FormatFlags) As String
429 MaxSize As DWord
430End Type
431
432/*!
433@brief 整数変換全てを行う関数。これを雛形とし、形式毎の差異はIntegerConvertTraitsで表現する。
434@author Egtra
435@date 2007/10/22
436@param[in] tr 特性情報。
437@param[in] x 変換元の数値。
438@param[in] d 精度。ここでは最低限出力する桁数。
439@param[in] field フィールド幅。
440@param[in] flags フラグ。
441*/
442Function FormatIntegerEx(ByRef tr As IntegerConvertTraits, x As QWord, d As DWord, field As DWord, flags As FormatFlags) As String
443 PreProcessFormatInteger(d, flags)
444
445 Dim sb = New System.Text.StringBuilder
446 With sb
447 Dim prefixFunc = tr.Prefix
448 Dim prefix = prefixFunc(x, flags)
449 sb.Append(prefix)
450
451 Dim prefixLen = 0 As DWord
452 If String.IsNullOrEmpty(prefix) = False Then
453 prefixLen = prefix.Length As DWord
454 End If
455
456 Dim buf = GC_malloc_atomic((tr.MaxSize + 1) * SizeOf (StrChar)) As *StrChar
457 Dim convertFunc = tr.Convert
458 Dim bufStartPos = convertFunc(buf, x, flags)
459
460 Dim len = (tr.MaxSize - bufStartPos) As Long
461 If len < d Then
462 .Append(&h30 As StrChar, d - len)
463 End If
464
465 .Append(buf, bufStartPos + 1, len)
466
467 AdjustFieldWidth(sb, field, flags And (Not (Sign Or Blank)), prefixLen)
468 End With
469 FormatIntegerEx = sb.ToString()
470
471 If (flags And Cap) = 0 Then
472 FormatIntegerEx = FormatIntegerEx.ToLower()
473 End If
474End Function
475
476'! QWordの最大値18446744073709551615の文字数 - 1。FormatIntegerLU内で使用。
477Const MaxSizeLU = 19
478
479/*!
480@brief 整数変換共通の前処理
481@author Egtra
482@date 2007/10/19
483@param[in, out] d 精度。DWORD_MAXで省略とみなされ、精度1になる。
484@param[in, out] flags フラグ。精度が指定されたとき、Zeroを消す。
485*/
486Sub PreProcessFormatInteger(ByRef d As DWord, ByRef flags As FormatFlags)
487 If d = DWORD_MAX Then
488 d = 1
489 Else
490 '精度が指定されているとき、ゼロフラグは無視される。
491 '仕様上、左揃えのときも無視されるが、それはAdjustFieldWidthが行ってくれる。
492 flags And= Not Zero
493 End If
494End Sub
495
496/*!
497@brief 文字列をフィールド幅まで満たされるように空白などを挿入する。
498@param [in,out] sb 対象文字列
499@param [in] field フィールド幅
500@param [in] hasSign 符号を持っている(負の値か)か否か
501@param [in] flags フラグ
502@param [in] prefixLen (あれば)接頭辞の文字数。ゼロ埋めする際、この数だけ挿入位置を後ろにする。
503sbが"-1"のように負符号を持っている場合は、呼出元でSignフラグ(またはBlank)を立てること。
504*/
505Sub AdjustFieldWidth(sb As System.Text.StringBuilder, field As DWord, flags As FormatFlags, prefixLen = 0 As DWord)
506 With sb
507 If .Length < field Then
508 Dim embeddedSize = field - .Length
509 If flags And Left Then
510 .Append(&h20, embeddedSize)
511 Else
512 Dim insPos As Long
513 If (flags And Zero) <> 0 Then
514 If (flags And Blank) Or (flags And Sign) Then
515 insPos++
516 End If
517 insPos += prefixLen
518 .Insert(insPos, String$(embeddedSize, "0"))
519 Else
520 .Insert(insPos, String$(embeddedSize, " "))
521 End If
522 End If
523 End If
524 End With
525End Sub
526
527End Namespace 'Detail
528
529End Namespace 'Strings
530End Namespace 'ActiveBasic
Note: See TracBrowser for help on using the repository browser.