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

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

QWord, Int64用書式化関数群を実装

File size: 17.3 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 sign = False
42 End If
43
44 '1e16 <= x < 1e17へ正規化
45 '(元のx) = (正規化後のx) ^ (d - 17)である。
46 Dim d = Math.Floor(Math.Log10(x)) As Long
47 If d < 16 Then
48 x *= ipow(10, +17 - d)
49 ElseIf d > 16 Then
50 x /= ipow(10, -17 + d)
51 End If
52
53 '補正
54 While x < 1e16
55 x *= 10
56 d--
57 Wend
58 While x >= 1e17
59 x /= 10
60 d++
61 Wend
62
63 d--
64 e = d
65
66 FloatToChars = FormatIntegerLU((x As Int64) As QWord, 17, 0, None)
67End Function
68
69/*!
70@brief 書式化関数群で使用するフラグ。
71@author Egtra
72@date 2007/09/18
73*/
74Const Enum FormatFlags
75 '! 何も指定がない。
76 None = &h0
77 /*!
78 符号、+。符号付変換[diAaEeFfGg]のとき、正の値でも符号を付ける。
79 AdjustFieldWidthの仕様から、Format関数郡内からAdjustFieldWidthにかけて、
80 単に数値が符号付である(負の値である)ことを示す意味でも用いられる。
81 */
82 Sign = &h1
83 /*! 空白、空白文字。
84 符号付変換[diAaEeFfGg]のとき、正の値ならば符号分の空白を開ける。Signが立っているときには無視される。
85 */
86 Blank = &h2
87 /*! ゼロ、0。
88 [diouXxAaEeFfGg]で、フィールドの空きを0で埋める。leftが立っているときには無視される。
89 */
90 Zero = &h4
91 '! 左揃え、-。フィールド内で左揃えにする。
92 Left = &h8
93 /*! 代替表記、#。
94 <ul>
95 <li>[OoXx]では、値が0でない場合、先頭に0、0xを付ける。</ul>
96 <li>[AaEeFfGg]では、精度0でも小数点を付ける。</ul>
97 <li>[Gg]では、それに加え、小数部末尾の0の省略を行わないようにする。</ul>
98 </ul>
99 */
100 Alt = &h10
101 '! 大文字。使用するアルファベットを大文字にする。[aefgx]を[AEFGX]化する。
102 Cap = &h20
103
104 /*!
105 内部処理用に予約。
106 @note Minusとして使用されている。
107 */
108 Reserved = &h80000000
109End Enum
110
111/*!
112@brief 浮動小数点数をprintfの%e, %E(指数形式、十進法)相当の変換で文字列化する関数。
113@author Egtra
114@date 2007/09/18
115@param[in] x 文字列化する浮動小数点数値。
116@param[in] d 精度。小数点以下の桁数。DWORD_MAXのとき、指定なしとして既定値6となる。
117@param[in] field フィールド幅。
118@param[in] flags 書式フラグ。
119@return xの文字列表現
120
121@todo 他の実装での末尾桁の扱いを調べる(このコードでは何もしていないので切捨となっている)。
122*/
123Function FormatFloatE(x As Double, d As DWord, field As DWord, flags As FormatFlags) As String
124 If d = DWORD_MAX Then
125 d = 6
126 End If
127
128 Dim e As Long, negative As Boolean
129 Dim s = FloatToChars(x, e, negative)
130
131 Dim sb = New System.Text.StringBuilder
132 With sb
133
134 AppendSign(sb, negative, flags)
135
136 .Append(s[0])
137
138 If (flags And Alt) Or d > 0 Then
139 .Append(".")
140 Dim outputLen = s.Length - 1
141 If outputLen >= d Then
142 .Append(s, 1, d)
143 Else 'sで用意された桁が指定された精度より少ないとき
144 .Append(s, 1, outputLen)
145 .Append(&h30 As StrChar, d - outputLen) '足りない桁は0埋め
146 End If
147 End If
148
149 If flags And Cap Then
150 .Append("E")
151 Else
152 .Append("e")
153 End If
154
155 .Append(FormatIntegerD(e, 2, 0, Sign Or Zero))
156
157 AdjustFieldWidth(sb, field, flags)
158 End With
159 FormatFloatE = sb.ToString()
160End Function
161
162/*!
163@brief 浮動小数点数をprintfの%f(小数形式、十進法)相当の変換で文字列化する関数。
164@author Egtra
165@date 2007/10/23
166@param[in] x 文字列化する浮動小数点数値。
167@param[in] precision 精度。小数点以下の桁数。DWORD_MAXのとき、指定なしとして既定値6となる。
168@param[in] field フィールド幅。
169@param[in] flags 書式フラグ。
170@return xの文字列表現
171*/
172Function FormatFloatF(x As Double, precision As DWord, field As DWord, flags As FormatFlags) As String
173 If precision = DWORD_MAX Then
174 precision = 6
175 End If
176
177 Dim e As Long, negative As Boolean
178 Dim s = FloatToChars(x, e, negative)
179
180 Dim sb = New System.Text.StringBuilder
181 With sb
182 AppendSign(sb, negative, flags)
183
184 Dim intPartLen = e + 1
185 Dim outputDigit = 0 As DWord
186 If intPartLen >= 17 Then
187 '有効桁が全て整数部に収まる場合
188 .Append(s)
189 .Append(&h30 As StrChar, intPartLen - 17)
190 outputDigit = 17
191 ElseIf intPartLen > 0 Then
192 '有効桁の一部が整数部にかかる場合
193 .Append(s, 0, intPartLen)
194 outputDigit = intPartLen
195 Else
196 '有効桁が全く整数部にかからない場合
197 .Append(&h30 As StrChar)
198 End If
199
200 If precision > 0 Or (flags And Alt) Then
201 .Append(".")
202
203 Dim lastDigit = s.Length - outputDigit
204 If lastDigit >= precision Then '変換して得られた文字列の桁数が精度以上ある場合
205 Dim zeroDigit = 0
206 If intPartLen < 0 Then
207 '1.23e-4 = 0.000123のように指数が負のため小数点以下に0が続く場合
208 zeroDigit = System.Math.Min(-intPartLen As DWord, precision)
209 .Append(&h30 As StrChar, zeroDigit As Long)
210 End If
211 .Append(s, outputDigit, (precision - zeroDigit) As Long)
212 Else
213 .Append(s, outputDigit, lastDigit)
214 .Append(&h30 As StrChar, (precision - lastDigit) As Long) '残りの桁は0埋め
215 End If
216 End If
217 AdjustFieldWidth(sb, field, flags)
218 End With
219 FormatFloatF = sb.ToString()
220End Function
221
222/*!
223@brief 先頭に符号もしくはその分の空白を出力する。FormatFloat用。
224@author Egtra
225@date 2007/10/23
226@param[in, out] sb 出力先
227@param[in] negative 符号
228@param[in, out] flags フラグ。negative = Trueなら、Signを立てて返す。
229*/
230Sub AppendSign(sb As System.Text.StringBuilder, negative As Boolean, ByRef flags As FormatFlags)
231 With sb
232 If negative Then
233 .Append("-")
234 flags Or= Sign
235 Else
236 If flags And Sign Then
237 .Append("+")
238 ElseIf flags And Blank Then
239 .Append(" ")
240 End If
241 End If
242 End With
243End Sub
244
245/*!
246@brief DWordの最大値4294967295の文字数 - 1。FormatIntegerU内で使用。
247@author Egtra
248@date 2007/09/18
249*/
250Const MaxSizeU = 9
251
252/*!
253@brief 符号無し整数をprintfの%u(十進法表現)相当の変換で文字列化する関数。
254@author Egtra
255@date 2007/09/18
256@param[in] x 文字列化する整数値。
257@param[in] d 精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。
258@param[in] field フィールド幅。
259@param[in] flags 書式フラグ。
260@return xの文字列表現
261*/
262Function FormatIntegerU(x As DWord, d As DWord, field As DWord, flags As FormatFlags) As String
263 Return FormatIntegerEx(TraitsIntegerDU, x, d, field, flags And (Not (Sign Or Blank)))
264End Function
265
266/*!
267@brief FormatIntegerUのQWord版
268@author Egtra
269@date 2007/10/26
270*/
271Function FormatIntegerLU(x As QWord, d As DWord, field As DWord, flags As FormatFlags) As String
272 Return FormatIntegerEx(TraitsIntegerLDU, x, d, field, flags And (Not (Sign Or Blank)))
273End Function
274
275/*!
276@brief 符号有り整数をprintfの%d(十進法表現)相当の変換で文字列化する関数。
277@author Egtra
278@date 2007/10/13
279@param[in] x 文字列化する整数値。
280@param[in] d 精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。
281@param[in] field フィールド幅。
282@param[in] flags 書式フラグ。
283@return xの文字列表現
284*/
285Function FormatIntegerD(x As Long, d As DWord, field As DWord, flags As FormatFlags) As String
286 Dim unsignedX As DWord
287 If x < 0 Then
288 unsignedX = (-x) As DWord
289 flags Or= Minus
290 Else
291 unsignedX = x As DWord
292 End If
293
294 Return FormatIntegerEx(TraitsIntegerDU, unsignedX, d, field, flags)
295End Function
296
297/*!
298@brief FormatIntegerDのInt64版
299@author Egtra
300@date 2007/10/26
301*/
302Function FormatIntegerLD(x As Int64, d As DWord, field As DWord, flags As FormatFlags) As String
303 Dim unsignedX As QWord
304 If x < 0 Then
305 unsignedX = (-x) As QWord
306 flags Or= Minus
307 Else
308 unsignedX = x As QWord
309 End If
310
311 Return FormatIntegerEx(TraitsIntegerLDU, unsignedX, d, field, flags)
312End Function
313
314/*!
315@author Egtra
316@date 2007/10/26
317*/
318Dim TraitsIntegerDU As IntegerConvertTraits
319With TraitsIntegerDU
320 .Convert = AddressOf(IntegerDU_Convert)
321 .Prefix = AddressOf(IntegerDU_Prefix)
322 .MaxSize = MaxSizeU
323End With
324
325/*!
326@author Egtra
327@date 2007/10/26
328*/
329Dim TraitsIntegerLDU As IntegerConvertTraits
330With TraitsIntegerLDU
331 .Convert = AddressOf(IntegerLDU_Convert)
332 .Prefix = AddressOf(IntegerDU_Prefix)
333 .MaxSize = MaxSizeLU
334End With
335
336/*!
337@brief 負数を表すフラグ。FormatIntegerD, LDからIntegerDU_Prefixまでの内部処理用。
338@author Egtra
339@date 2007/10/26
340*/
341Const Minus = Reserved
342
343/*!
344@author Egtra
345@date 2007/10/26
346*/
347Function IntegerDU_Convert(buf As *StrChar, xq As QWord, flags As FormatFlags) As DWord
348 Dim x = xq As DWord
349 Dim i = MaxSizeU
350 While x <> 0
351 buf[i] = (x As Int64 Mod 10 + &h30) As StrChar 'Int64への型変換は#117対策
352 x \= 10
353 i--
354 Wend
355 Return i
356End Function
357
358/*!
359@brief IntegerDU_ConvertのQWord版
360@author Egtra
361@date 2007/10/26
362@bug #117のため、現在Int64の最大値を超える値を正しく処理できない。
363*/
364Function IntegerLDU_Convert(buf As *StrChar, x As QWord, flags As FormatFlags) As DWord
365 Dim i = MaxSizeLU
366 While x <> 0
367 buf[i] = (x As Int64 Mod 10 + &h30) As StrChar 'Int64への型変換は#117対策
368 x \= 10
369 i--
370 Wend
371 Return i
372End Function
373
374/*!
375@author Egtra
376@date 2007/10/26
377*/
378Function IntegerDU_Prefix(x As QWord, flags As FormatFlags) As String
379 If flags And Minus Then
380 IntegerDU_Prefix = "-"
381 ElseIf flags And Sign Then
382 IntegerDU_Prefix = "+"
383 ElseIf flags And Blank Then
384 IntegerDU_Prefix = " "
385 End If
386End Function
387
388/*!
389@brief DWordの最大値の八進法表現37777777777の文字数 - 1 + 1。IntegerO_Convert内で使用。
390@author Egtra
391@date 2007/10/19
392上の式で1を加えているのは、八進接頭辞の分。
393*/
394Const MaxSizeO = 11
395
396/*!
397@brief QWordの最大値の八進法表現1777777777777777777777の文字数 - 1 + 1。IntegerO_Convert内で使用。
398@author Egtra
399@date 2007/10/26
400上の式で1を加えているのは、八進接頭辞の分。
401*/
402Const MaxSizeLO = 22
403
404/*!
405@author Egtra
406@date 2007/10/22
407*/
408Dim TraitsIntegerO As IntegerConvertTraits
409With TraitsIntegerO
410 .Convert = AddressOf(IntegerO_Convert)
411 .Prefix = AddressOf(IntegerO_Prefix)
412 .MaxSize = MaxSizeO
413End With
414
415/*!
416@author Egtra
417@date 2007/10/26
418*/
419Dim TraitsIntegerLO As IntegerConvertTraits
420With TraitsIntegerLO
421 .Convert = AddressOf(IntegerLO_Convert)
422 .Prefix = AddressOf(IntegerO_Prefix)
423 .MaxSize = MaxSizeLO
424End With
425
426/*!
427@brief 符号無し整数をprintfの%o(八進法表現)相当の変換で文字列化する関数。
428@author Egtra
429@date 2007/10/19
430@param[in] x 文字列化する整数値。
431@param[in] d 精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。
432@param[in] field フィールド幅。
433@param[in] flags 書式フラグ。
434@return xの文字列表現
435*/
436Function FormatIntegerO(x As DWord, d As DWord, field As DWord, flags As FormatFlags) As String
437 Return FormatIntegerEx(TraitsIntegerO, x, d, field, flags)
438End Function
439
440/*!
441@brief FormatIntegerOのQWord版。
442@author Egtra
443@date 2007/10/26
444*/
445Function FormatIntegerLO(x As QWord, d As DWord, field As DWord, flags As FormatFlags) As String
446 Return FormatIntegerEx(TraitsIntegerLO, x, d, field, flags)
447End Function
448
449/*!
450@author Egtra
451@date 2007/10/22
452*/
453Function IntegerO_Convert(buf As *StrChar, xq As QWord, flags As FormatFlags) As DWord
454 Dim x = xq As DWord
455 Dim i = MaxSizeO
456 While x <> 0
457 buf[i] = ((x And &o7) + &h30) As StrChar
458 x >>= 3
459 i--
460 Wend
461 If flags And Alt Then
462 buf[i] = &h30
463 i--
464 End If
465 Return i
466End Function
467
468/*!
469@brief IntegerO_ConvertのQWord版。
470@author Egtra
471@date 2007/10/26
472*/
473Function IntegerLO_Convert(buf As *StrChar, x As QWord, flags As FormatFlags) As DWord
474 Dim i = MaxSizeLO
475 While x <> 0
476 buf[i] = ((x And &o7) + &h30) As StrChar
477 x >>= 3
478 i--
479 Wend
480 If flags And Alt Then
481 buf[i] = &h30
482 i--
483 End If
484 Return i
485End Function
486
487/*!
488@author Egtra
489@date 2007/10/22
490@note #フラグ (Alt)の処理は、IntegerO/LO_Convert内で行うので、ここで処理することはない。
491*/
492Function IntegerO_Prefix(x As QWord, flags As FormatFlags) As String
493End Function
494
495/*!
496@brief DWordの最大値の十六進法表現ffffffffの文字数 - 1。FormatIntegerO内で使用。
497@author Egtra
498@date 2007/10/24
499*/
500Const MaxSizeX = 7
501
502/*!
503@brief QWordの最大値の十六進法表現ffffffffffffffffの文字数 - 1。FormatIntegerO内で使用。
504@author Egtra
505@date 2007/10/26
506*/
507Const MaxSizeLX = 15
508
509/*!
510@author Egtra
511@date 2007/10/24
512*/
513Dim TraitsIntegerX As IntegerConvertTraits
514With TraitsIntegerX
515 .Convert = AddressOf(IntegerX_Convert)
516 .Prefix = AddressOf(IntegerX_Prefix)
517 .MaxSize = MaxSizeX
518End With
519
520/*!
521@author Egtra
522@date 2007/10/26
523*/
524Dim TraitsIntegerLX As IntegerConvertTraits
525With TraitsIntegerLX
526 .Convert = AddressOf(IntegerLX_Convert)
527 .Prefix = AddressOf(IntegerX_Prefix)
528 .MaxSize = MaxSizeLX
529End With
530
531/*!
532@brief 整数をprintfの%x, %X(十六進法)相当の変換で文字列化する関数。
533@author Egtra
534@date 2007/10/19
535@param[in] x 文字列化する整数値。
536@param[in] d 精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。
537@param[in] field フィールド幅。
538@param[in] flags 書式フラグ。
539@return xの文字列表現
540*/
541Function FormatIntegerX(x As DWord, d As DWord, field As DWord, flags As FormatFlags) As String
542 Return FormatIntegerEx(TraitsIntegerX, x, d, field, flags)
543End Function
544
545/*!
546@brief FormatIntegerXのQWord版。
547@author Egtra
548@date 2007/10/22
549*/
550Function FormatIntegerLX(x As QWord, d As DWord, field As DWord, flags As FormatFlags) As String
551 Return FormatIntegerEx(TraitsIntegerLX, x, d, field, flags)
552End Function
553
554/*!
555@author Egtra
556@date 2007/10/22
557*/
558Function IntegerX_Convert(buf As *StrChar, xq As QWord, flags As FormatFlags) As DWord
559 Dim i = MaxSizeX
560 Dim x = xq As DWord
561 While x <> 0
562 buf[i] = _System_HexadecimalTable[x And &h0f]
563 x >>= 4
564 i--
565 Wend
566 Return i
567End Function
568
569/*!
570@brief IntegerX_ConvertのQWord版。
571@author Egtra
572@date 2007/10/22
573*/
574Function IntegerLX_Convert(buf As *StrChar, x As QWord, flags As FormatFlags) As DWord
575 Dim i = MaxSizeLX
576 While x <> 0
577 buf[i] = _System_HexadecimalTable[x And &h0f]
578 x >>= 4
579 i--
580 Wend
581 Return i
582End Function
583
584/*!
585@author Egtra
586@date 2007/10/24
587*/
588Function IntegerX_Prefix(x As QWord, flags As FormatFlags) As String
589 If flags And Alt Then
590 If x <> 0 Then
591 IntegerX_Prefix = "0X"
592 End If
593 End If
594End Function
595
596/*!
597@brief FormatIntegerExへ渡す変換特性を表す構造体型。
598@author Egtra
599@date 2007/10/22
600*/
601Type IntegerConvertTraits
602 '!変換を行う関数へのポインタ。
603 Convert As *Function(buf As *StrChar, x As QWord, flags As FormatFlags) As DWord
604 '!接頭辞を取得する関数へのポインタ。
605 Prefix As *Function(x As QWord, flags As FormatFlags) As String
606 '!必要なバッファの大きさ。
607 MaxSize As DWord
608End Type
609
610/*!
611@brief 整数変換全てを行う関数。これを雛形とし、形式毎の差異はIntegerConvertTraitsで表現する。
612@author Egtra
613@date 2007/10/22
614@param[in] tr 特性情報。
615@param[in] x 変換元の数値。
616@param[in] d 精度。ここでは最低限出力する桁数。
617@param[in] field フィールド幅。
618@param[in] flags フラグ。
619*/
620Function FormatIntegerEx(ByRef tr As IntegerConvertTraits, x As QWord, d As DWord, field As DWord, flags As FormatFlags) As String
621 If d = DWORD_MAX Then
622 d = 1
623 Else
624 '精度が指定されているとき、ゼロフラグは無視される。
625 '仕様上、左揃えのときも無視されるが、それはAdjustFieldWidthが行ってくれる。
626 flags And= Not Zero
627 End If
628
629 Dim sb = New System.Text.StringBuilder
630 With sb
631 Dim prefixFunc = tr.Prefix
632 Dim prefix = prefixFunc(x, flags)
633 sb.Append(prefix)
634
635 Dim prefixLen = 0 As DWord
636 If String.IsNullOrEmpty(prefix) = False Then
637 prefixLen = prefix.Length As DWord
638 End If
639
640 Dim buf = GC_malloc_atomic((tr.MaxSize + 1) * SizeOf (StrChar)) As *StrChar
641 Dim convertFunc = tr.Convert
642 Dim bufStartPos = convertFunc(buf, x, flags)
643
644 Dim len = (tr.MaxSize - bufStartPos) As Long
645 If len < 0 Then
646 Debug
647 End If
648 If len < d Then
649 .Append(&h30 As StrChar, d - len)
650 End If
651
652 .Append(buf, bufStartPos + 1, len)
653
654 AdjustFieldWidth(sb, field, flags And (Not (Sign Or Blank)), prefixLen)
655 End With
656 FormatIntegerEx = sb.ToString()
657
658 If (flags And Cap) = 0 Then
659 FormatIntegerEx = FormatIntegerEx.ToLower()
660 End If
661End Function
662
663/*!
664@brief QWordの最大値18446744073709551615の文字数 - 1。FormatIntegerLU内で使用。
665@author Egtra
666@date 2007/09/18
667*/
668Const MaxSizeLU = 19
669
670/*!
671@brief 文字列をフィールド幅まで満たされるように空白などを挿入する。
672@author Egtra
673@date 2007/10/13
674@param[in,out] sb 対象文字列
675@param[in] field フィールド幅
676@param[in] hasSign 符号を持っている(負の値か)か否か
677@param[in] flags フラグ
678@param[in] prefixLen (あれば)接頭辞の文字数。ゼロ埋めする際、この数だけ挿入位置を後ろにする。
679sbが"-1"のように負符号を持っている場合は、呼出元でSignフラグ(またはBlank)を立てること。
680*/
681Sub AdjustFieldWidth(sb As System.Text.StringBuilder, field As DWord, flags As FormatFlags, prefixLen = 0 As DWord)
682 With sb
683 If .Length < field Then
684 Dim embeddedSize = field - .Length
685 If flags And Left Then
686 .Append(&h20, embeddedSize)
687 Else
688 Dim insPos As Long
689 If (flags And Zero) <> 0 Then
690 If (flags And Blank) Or (flags And Sign) Then
691 insPos++
692 End If
693 insPos += prefixLen
694 .Insert(insPos, String$(embeddedSize, "0"))
695 Else
696 .Insert(insPos, String$(embeddedSize, " "))
697 End If
698 End If
699 End If
700 End With
701End Sub
702
703End Namespace 'Detail
704
705End Namespace 'Strings
706End Namespace 'ActiveBasic
Note: See TracBrowser for help on using the repository browser.