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
RevLine 
[335]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
[364]18xに無限大、非数を渡した場合の動作は未定義。
[335]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
[364]78 /*!
79 符号、+。符号付変換[diAaEeFfGg]のとき、正の値でも符号を付ける。
80 AdjustFieldWidthの仕様から、Format関数郡内からAdjustFieldWidthにかけて、
81 単に数値が符号付である(負の値である)ことを示す意味でも用いられる。
82 */
[335]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>
[358]96 <li>[OoXx]では、値が0でない場合、先頭に0、0xを付ける。</ul>
[335]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/*!
[364]107@brief 浮動小数点数をprintfの%e, %E(指数形式、十進法)相当の変換で文字列化する関数。
[335]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
[364]116@todo 他の実装での末尾桁の扱いを調べる(このコードでは何もしていないので切捨となっている)。
[335]117*/
118Function FormatFloatE(x As Double, d As DWord, field As DWord, flags As FormatFlags) As String
[364]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
[335]126 Dim sb = New System.Text.StringBuilder
127 With sb
128
[364]129 AppendSign(sb, negative, flags)
[335]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
[355]150 .Append(FormatIntegerD(e, 2, 0, Sign Or Zero))
[335]151
[364]152 AdjustFieldWidth(sb, field, flags)
[335]153 End With
154 FormatFloatE = sb.ToString()
155End Function
156
[364]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
[335]240'! DWordの最大値4294967295の文字数 - 1。FormatIntegerU内で使用。
[355]241Const MaxSizeU = 9
[335]242
243/*!
[364]244@brief 符号無し整数をprintfの%u(十進法表現)相当の変換で文字列化する関数。
[335]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
[358]254 FormatIntegerU = FormatInteger(x, d, field, flags And (Not (Sign Or Blank)), 0)
[355]255End Function
[335]256
[358]257/*!
258DWordの最大値4294967295やLongの最大値2147483647、最小値-2147483648
259(全て十進法)の符号部を除いた文字数 - 1。FormatIntegerU内で使用。
260*/
[355]261Const MaxSizeD = 9
262
263/*!
[364]264@brief 符号有り整数をprintfの%d(十進法表現)相当の変換で文字列化する関数。
[355]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("-")
[364]280 flags Or= Sign
[355]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の文字列表現
[364]303
304signCharに何らかの値を指定するときには、必ずflagsにSignまたはBlankを指定すること。
305さもないと、フィールド幅を指定したときに正しく整形されなくなる。
[355]306*/
307Function FormatInteger(x As DWord, d As DWord, field As DWord, flags As FormatFlags, signChar As StrChar) As String
[358]308 PreProcessFormatInteger(d, flags)
[335]309
310 Dim sb = New System.Text.StringBuilder
311 With sb
[355]312 If signChar <> 0 Then
313 .Append(signChar)
314 End If
315
316 Dim buf[MaxSizeU] As StrChar
317 Dim i = MaxSizeU
[335]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
[355]324 Dim len = (MaxSizeU - i) As Long
[335]325 If len < d Then
326 .Append(&h30 As StrChar, d - len)
327 End If
328
329 .Append(buf, i + 1, len)
330
[364]331 AdjustFieldWidth(sb, field, flags)
[355]332 End With
333 FormatInteger = sb.ToString()
334End Function
335
[358]336/*!
[364]337DWordの最大値の八進法表現37777777777の文字数 - 1 + 1。FormatIntegerO内で使用。
338上の式で1を加えているのは、八進接頭辞の分。
[358]339*/
[364]340Const MaxSizeO = 11
[358]341
[364]342Dim TraitsIntegerO As IntegerConvertTraits
343With TraitsIntegerO
344 .Convert = AddressOf(IntegerO_Convert)
345 .Prefix = AddressOf(IntegerO_Prefix)
346 .MaxSize = MaxSizeO
347End With
348
[358]349/*!
[364]350@brief 符号無し整数をprintfの%o(八進法表現)相当の変換で文字列化する関数。
[358]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
[364]360 FormatIntegerO = FormatIntegerEx(TraitsIntegerO, x, d, field, flags)
361End Function
[358]362
[364]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
[358]377
[364]378Function IntegerO_Prefix(x As QWord, flags As FormatFlags) As String
[358]379End Function
380
381/*!
382DWordの最大値の十六進法表現ffffffffの文字数 - 1。FormatIntegerO内で使用。
383*/
384Const MaxSizeX = 7
385
[364]386Dim TraitsIntegerX As IntegerConvertTraits
387With TraitsIntegerX
388 .Convert = AddressOf(IntegerX_Convert)
389 .Prefix = AddressOf(IntegerX_Prefix)
390 .MaxSize = MaxSizeX
391End With
392
[358]393/*!
[364]394@brief 整数をprintfの%x, %X(十六進法)相当の変換で文字列化する関数。
[358]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
[364]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
[358]443 PreProcessFormatInteger(d, flags)
444
445 Dim sb = New System.Text.StringBuilder
446 With sb
[364]447 Dim prefixFunc = tr.Prefix
448 Dim prefix = prefixFunc(x, flags)
449 sb.Append(prefix)
450
[358]451 Dim prefixLen = 0 As DWord
[364]452 If String.IsNullOrEmpty(prefix) = False Then
453 prefixLen = prefix.Length As DWord
[358]454 End If
455
[364]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)
[358]459
[364]460 Dim len = (tr.MaxSize - bufStartPos) As Long
[358]461 If len < d Then
462 .Append(&h30 As StrChar, d - len)
463 End If
464
[364]465 .Append(buf, bufStartPos + 1, len)
[358]466
[364]467 AdjustFieldWidth(sb, field, flags And (Not (Sign Or Blank)), prefixLen)
[358]468 End With
[364]469 FormatIntegerEx = sb.ToString()
[358]470
471 If (flags And Cap) = 0 Then
[364]472 FormatIntegerEx = FormatIntegerEx.ToLower()
[358]473 End If
474End Function
475
[355]476'! QWordの最大値18446744073709551615の文字数 - 1。FormatIntegerLU内で使用。
477Const MaxSizeLU = 19
478
479/*!
[358]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 '精度が指定されているとき、ゼロフラグは無視される。
[364]491 '仕様上、左揃えのときも無視されるが、それはAdjustFieldWidthが行ってくれる。
[358]492 flags And= Not Zero
493 End If
494End Sub
495
496/*!
[355]497@brief 文字列をフィールド幅まで満たされるように空白などを挿入する。
[358]498@param [in,out] sb 対象文字列
499@param [in] field フィールド幅
500@param [in] hasSign 符号を持っている(負の値か)か否か
501@param [in] flags フラグ
502@param [in] prefixLen (あれば)接頭辞の文字数。ゼロ埋めする際、この数だけ挿入位置を後ろにする。
[364]503sbが"-1"のように負符号を持っている場合は、呼出元でSignフラグ(またはBlank)を立てること。
[355]504*/
[364]505Sub AdjustFieldWidth(sb As System.Text.StringBuilder, field As DWord, flags As FormatFlags, prefixLen = 0 As DWord)
[355]506 With sb
507 If .Length < field Then
508 Dim embeddedSize = field - .Length
[335]509 If flags And Left Then
510 .Append(&h20, embeddedSize)
511 Else
[355]512 Dim insPos As Long
513 If (flags And Zero) <> 0 Then
[364]514 If (flags And Blank) Or (flags And Sign) Then
[355]515 insPos++
516 End If
[358]517 insPos += prefixLen
[355]518 .Insert(insPos, String$(embeddedSize, "0"))
519 Else
520 .Insert(insPos, String$(embeddedSize, " "))
521 End If
[335]522 End If
523 End If
524 End With
[355]525End Sub
[335]526
527End Namespace 'Detail
528
529End Namespace 'Strings
530End Namespace 'ActiveBasic
Note: See TracBrowser for help on using the repository browser.