'Classes/ActiveBasic/Strings/SPrintF.ab
Namespace ActiveBasic
Namespace Strings
Namespace Detail
/*!
@brief 浮動小数点数を文字列化する低水準な関数。符号、指数、仮数に分けて出力。
@author Egtra
@date 2007/09/18
@param[in] x 文字列化する浮動小数点数
@param[out] e 指数
@param[out] sign 符号
@return 仮数
仮数は1の位から下へ17桁で、小数点を含まない。そのため、誤差を無視すればVal(仮数) * 10 ^ (e - 17) = Abs(x)が成り立つ。
xに無限大、無限小、非数を渡した場合の動作は未定義。
*/
Function FloatToChars(x As Double, ByRef e As Long, ByRef sign As Boolean) As String
Imports System
'0を弾く
If x = 0 Then
If GetQWord(VarPtr(x) As *QWord) And &h8000000000000000 Then
sign = True
Else
sign = False
End If
e = 0
FloatToChars = "00000000000000000"
Exit Function
End If
'符号の判断(同時に符号を取り除く)
If x < 0 Then
sign = True
x = -x
Else
sign = False
End If
'1e16 <= x < 1e17へ正規化
'(元のx) = (正規化後のx) ^ (d - 17)である。
Dim d = Math.Floor(Math.Log10(x)) As Long
If d < 16 Then
x *= ipow(10, +17 - d)
ElseIf d > 16 Then
x /= ipow(10, -17 + d)
End If
'補正
While x < 1e16
x *= 10
d--
Wend
While x >= 1e17
x /= 10
d++
Wend
d--
e = d
FloatToChars = Str$(x As QWord)
End Function
/*!
@brief 書式化関数群で使用するフラグ。
@author Egtra
@date 2007/09/18
*/
Const Enum FormatFlags
'! 何も指定がない。
None = &h0
'! 符号、+。符号付変換[diAaEeFfGg]のとき、正の値でも符号を付ける。
Sign = &h1
/*! 空白、空白文字。
符号付変換[diAaEeFfGg]のとき、正の値ならば符号分の空白を開ける。Signが立っているときには無視される。
*/
Blank = &h2
/*! ゼロ、0。
[diouXxAaEeFfGg]で、フィールドの空きを0で埋める。leftが立っているときには無視される。
*/
Zero = &h4
'! 左揃え、-。フィールド内で左揃えにする。
Left = &h8
/*! 代替表記、#。
[AaEeFfGg]では、精度0でも小数点を付ける。
[Gg]では、それに加え、小数部末尾の0の省略を行わないようにする。
*/
Alt = &h10
'! 大文字。使用するアルファベットを大文字にする。[aefgx]を[AEFGX]化する。
Cap = &h20
End Enum
/*!
@brief 浮動小数点数をprintfの%e, %E相当の変換で文字列化する関数。
@author Egtra
@date 2007/09/18
@param[in] x 文字列化する浮動小数点数値。
@param[in] d 精度。小数点以下の桁数。DWORD_MAXのとき、指定なしとして既定値6となる。
@param[in] field フィールド幅。
@param[in] flags 書式フラグ。
@return xの文字列表現
@todo 末尾桁四捨五入
*/
Function FormatFloatE(x As Double, d As DWord, field As DWord, flags As FormatFlags) As String
Dim sb = New System.Text.StringBuilder
With sb
If d = DWORD_MAX Then
d = 6
End If
Dim e As Long, sign As Boolean
Dim s = FloatToChars(x, e, sign)
If sign Then
.Append("-")
Else
If flags And Sign Then
.Append("+")
ElseIf flags And Blank Then
.Append(" ")
End If
End If
.Append(s[0])
If (flags And Alt) Or d > 0 Then
.Append(".")
Dim outputLen = s.Length - 1
If outputLen >= d Then
.Append(s, 1, d)
Else 'sで用意された桁が指定された精度より少ないとき
.Append(s, 1, outputLen)
.Append(&h30 As StrChar, d - outputLen) '足りない桁は0埋め
End If
End If
If flags And Cap Then
.Append("E")
Else
.Append("e")
End If
Dim buf[1023] As TCHAR
wsprintf(buf, "%03d", e)' "%+03d", e) wsprintfは+フラグが使えない!
Dim ts = New String(buf, 3)
.Append(ts)
'.Append(FormatLongD(e, 3, 0, Sign)
If .Length < field Then
Dim embeddedSize = .Length - field
If flags And Left Then
.Append(&h20, embeddedSize)
Else
Dim insPos As Long
If flags And Blank Then
insPos++
End If
.Insert(insPos, String$(embeddedSize, " "))
End If
End If
End With
FormatFloatE = sb.ToString()
End Function
'! DWordの最大値4294967295の文字数 - 1。FormatIntegerU内で使用。
Const MaxSize = 9
/*!
@brief 符号無し整数をprintfの%u相当の変換で文字列化する関数。
@author Egtra
@date 2007/09/18
@param[in] x 文字列化する整数値。
@param[in] d 精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。
@param[in] field フィールド幅。
@param[in] flags 書式フラグ。
@return xの文字列表現
*/
Function FormatIntegerU(x As DWord, d As DWord, field As DWord, flags As FormatFlags) As String
'zero, left
'左揃えのときまたは精度が指定されているとき、ゼロフラグは無視される。
If (flags And Left) Or (d <> DWORD_MAX) Then
flags And= Not Zero
End If
If d = DWORD_MAX Then
d = 1
End If
Dim sb = New System.Text.StringBuilder
With sb
Dim buf[MaxSize] As StrChar
Dim i = MaxSize
While x <> 0
buf[i] = (x As Int64 Mod 10 + &h30) As StrChar 'Int64への型変換は#117対策
x \= 10
i--
Wend
Dim len = (MaxSize - i) As Long
If len < d Then
.Append(&h30 As StrChar, d - len)
End If
.Append(buf, i + 1, len)
If field > len Then
Dim embeddedSize = field - len
If flags And Left Then
.Append(&h20, embeddedSize)
Else
.Insert(0, String$(embeddedSize, " "))
End If
End If
End With
FormatIntegerU = sb.ToString()
End Function
End Namespace 'Detail
End Namespace 'Strings
End Namespace 'ActiveBasic