'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, negative As Boolean Dim s = FloatToChars(x, e, negative) If negative 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 .Append(FormatIntegerD(e, 2, 0, Sign Or Zero)) AdjustFieldWidth(sb, field, negative, flags) End With FormatFloatE = sb.ToString() End Function '! DWordの最大値4294967295の文字数 - 1。FormatIntegerU内で使用。 Const MaxSizeU = 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 FormatIntegerU = FormatInteger(x, d, field, flags And (Not (Sign Or Blank)), 0) End Function /*! DWordの最大値4294967295やLongの最大値2147483647、最小値-2147483648 (全て十進法)の符号部を除いた文字数 - 1。FormatIntegerU内で使用。 */ Const MaxSizeD = 9 /*! @brief 符号有り整数をprintfの%u相当の変換で文字列化する関数。 @author Egtra @date 2007/10/13 @param[in] x 文字列化する整数値。 @param[in] d 精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。 @param[in] field フィールド幅。 @param[in] flags 書式フラグ。 @return xの文字列表現 */ Function FormatIntegerD(x As Long, d As DWord, field As DWord, flags As FormatFlags) As String Dim dwX As DWord Dim signChar As StrChar If x < 0 Then dwX = (-x) As DWord signChar = Asc("-") Else dwX = x As DWord If flags And Sign Then signChar = Asc("+") ElseIf flags And Blank Then signChar = Asc(" ") End If End If FormatIntegerD = FormatInteger(dwX, d, field, flags, signChar) End Function /*! @brief 整数をprintfの%d, %u相当の変換で文字列化する関数。 @author Egtra @date 2007/09/18 @param[in] x 文字列化する整数値。 @param[in] d 精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。 @param[in] field フィールド幅。 @param[in] flags 書式フラグ。 @param[in] signChar 符号部分の文字。\0なら存在しないとして扱われる。 @return xの文字列表現 */ Function FormatInteger(x As DWord, d As DWord, field As DWord, flags As FormatFlags, signChar As StrChar) As String PreProcessFormatInteger(d, flags) Dim sb = New System.Text.StringBuilder With sb If signChar <> 0 Then .Append(signChar) End If Dim buf[MaxSizeU] As StrChar Dim i = MaxSizeU While x <> 0 buf[i] = (x As Int64 Mod 10 + &h30) As StrChar 'Int64への型変換は#117対策 x \= 10 i-- Wend Dim len = (MaxSizeU - i) As Long If len < d Then .Append(&h30 As StrChar, d - len) End If .Append(buf, i + 1, len) AdjustFieldWidth(sb, field, signChar <> 0, flags) End With FormatInteger = sb.ToString() End Function /*! DWordの最大値の八進法表現37777777777の文字数 - 1。FormatIntegerO内で使用。 */ Const MaxSizeO = 10 /*! @brief 整数をprintfの%o相当の変換で文字列化する関数。 @author Egtra @date 2007/10/19 @param[in] x 文字列化する整数値。 @param[in] d 精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。 @param[in] field フィールド幅。 @param[in] flags 書式フラグ。 @return xの文字列表現 */ Function FormatIntegerO(x As DWord, d As DWord, field As DWord, flags As FormatFlags) As String PreProcessFormatInteger(d, flags) Dim sb = New System.Text.StringBuilder With sb Dim buf[MaxSizeO] As StrChar Dim i = MaxSizeO While x <> 0 buf[i] = ((x And &o7) + &h30) As StrChar x >>= 3 i-- Wend Dim len = (MaxSizeO - i) As Long If flags And Alt Then .Append(&h30 As StrChar) len++ End If If len < d Then .Append(&h30 As StrChar, d - len) End If .Append(buf, i + 1, len) AdjustFieldWidth(sb, field, False, flags And (Not (Sign Or Blank))) End With FormatIntegerO = sb.ToString() End Function /*! DWordの最大値の十六進法表現ffffffffの文字数 - 1。FormatIntegerO内で使用。 */ Const MaxSizeX = 7 /*! @brief 整数をprintfの%x, %X相当の変換で文字列化する関数。 @author Egtra @date 2007/10/19 @param[in] x 文字列化する整数値。 @param[in] d 精度、最小限表示される桁数。DWORD_MAXのとき、指定なしとして、既定値1となる。 @param[in] field フィールド幅。 @param[in] flags 書式フラグ。 @return xの文字列表現 */ Function FormatIntegerX(x As DWord, d As DWord, field As DWord, flags As FormatFlags) As String PreProcessFormatInteger(d, flags) Dim sb = New System.Text.StringBuilder With sb Dim prefixLen = 0 As DWord If (flags And Alt) Then If x <> 0 Then .Append("0X") prefixLen = 2 End If End If Dim buf[MaxSizeX] As StrChar Dim i = MaxSizeX While x <> 0 buf[i] = _System_HexadecimalTable[x And &h0f] x >>= 4 i-- Wend Dim len = (MaxSizeX - i) As Long If len < d Then .Append(&h30 As StrChar, d - len) End If .Append(buf, i + 1, len) AdjustFieldWidth(sb, field, False, flags And (Not (Sign Or Blank)), prefixLen) End With FormatIntegerX = sb.ToString() If (flags And Cap) = 0 Then FormatIntegerX = FormatIntegerX.ToLower() End If End Function '! QWordの最大値18446744073709551615の文字数 - 1。FormatIntegerLU内で使用。 Const MaxSizeLU = 19 /*! @brief 整数変換共通の前処理 @author Egtra @date 2007/10/19 @param[in, out] d 精度。DWORD_MAXで省略とみなされ、精度1になる。 @param[in, out] flags フラグ。精度が指定されたとき、Zeroを消す。 */ Sub PreProcessFormatInteger(ByRef d As DWord, ByRef flags As FormatFlags) If d = DWORD_MAX Then d = 1 Else '精度が指定されているとき、ゼロフラグは無視される。 '左揃えのときも無視されるが、それはAdjustFieldWidthが行ってくれる。 flags And= Not Zero End If End Sub /*! @brief 文字列をフィールド幅まで満たされるように空白などを挿入する。 @param [in,out] sb 対象文字列 @param [in] field フィールド幅 @param [in] hasSign 符号を持っている(負の値か)か否か @param [in] flags フラグ @param [in] prefixLen (あれば)接頭辞の文字数。ゼロ埋めする際、この数だけ挿入位置を後ろにする。 */ Sub AdjustFieldWidth(sb As System.Text.StringBuilder, field As DWord, hasSign As Boolean, flags As FormatFlags, prefixLen = 0 As DWord) With sb If .Length < field Then Dim embeddedSize = field - .Length If flags And Left Then .Append(&h20, embeddedSize) Else Dim insPos As Long If (flags And Zero) <> 0 Then If ((flags And Blank) Or (flags And Sign) Or hasSign) Then insPos++ End If insPos += prefixLen .Insert(insPos, String$(embeddedSize, "0")) Else .Insert(insPos, String$(embeddedSize, " ")) End If End If End If End With End Sub End Namespace 'Detail End Namespace 'Strings End Namespace 'ActiveBasic