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

Last change on this file since 365 was 365, checked in by イグトランス (egtra), 16 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.