Ignore:
Timestamp:
Oct 24, 2007, 12:32:20 AM (17 years ago)
Author:
イグトランス (egtra)
Message:

FormatFloatFを実装

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Include/Classes/ActiveBasic/Strings/SPrintF.ab

    r359 r364  
    1616仮数は1の位から下へ17桁で、小数点を含まない。そのため、誤差を無視すればVal(仮数) * 10 ^ (e - 17) = Abs(x)が成り立つ。
    1717
    18 xに無限大、無限小、非数を渡した場合の動作は未定義。
     18xに無限大、非数を渡した場合の動作は未定義。
    1919*/
    2020Function FloatToChars(x As Double, ByRef e As Long, ByRef sign As Boolean) As String
     
    7676    '! 何も指定がない。
    7777    None = &h0
    78     '! 符号、+。符号付変換[diAaEeFfGg]のとき、正の値でも符号を付ける。
     78    /*!
     79    符号、+。符号付変換[diAaEeFfGg]のとき、正の値でも符号を付ける。
     80    AdjustFieldWidthの仕様から、Format関数郡内からAdjustFieldWidthにかけて、
     81    単に数値が符号付である(負の値である)ことを示す意味でも用いられる。
     82    */
    7983    Sign = &h1
    8084    /*! 空白、空白文字。
     
    101105
    102106/*!
    103 @brief  浮動小数点数をprintfの%e, %E相当の変換で文字列化する関数。
     107@brief  浮動小数点数をprintfの%e, %E(指数形式、十進法)相当の変換で文字列化する関数。
    104108@author Egtra
    105109@date   2007/09/18
     
    110114@return xの文字列表現
    111115
    112 @todo 末尾桁四捨五入
     116@todo 他の実装での末尾桁の扱いを調べる(このコードでは何もしていないので切捨となっている)。
    113117*/
    114118Function FormatFloatE(x As Double, d As DWord, field As DWord, flags As FormatFlags) As String
     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
    115126    Dim sb = New System.Text.StringBuilder
    116127    With sb
    117         If d = DWORD_MAX Then
    118             d = 6
    119         End If
    120 
    121         Dim e As Long, negative As Boolean
    122         Dim s = FloatToChars(x, e, negative)
    123 
    124         If negative Then
    125             .Append("-")
    126         Else
    127             If flags And Sign Then
    128                 .Append("+")
    129             ElseIf flags And Blank Then
    130                 .Append(" ")
    131             End If
    132         End If
     128
     129        AppendSign(sb, negative, flags)
    133130
    134131        .Append(s[0])
     
    153150        .Append(FormatIntegerD(e, 2, 0, Sign Or Zero))
    154151
    155         AdjustFieldWidth(sb, field, negative, flags)
     152        AdjustFieldWidth(sb, field, flags)
    156153    End With
    157154    FormatFloatE = sb.ToString()
    158155End Function
    159156
     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
    160240'! DWordの最大値4294967295の文字数 - 1。FormatIntegerU内で使用。
    161241Const MaxSizeU = 9
    162242
    163243/*!
    164 @brief  符号無し整数をprintfの%u相当の変換で文字列化する関数。
     244@brief  符号無し整数をprintfの%u(十進法表現)相当の変換で文字列化する関数。
    165245@author Egtra
    166246@date   2007/09/18
     
    182262
    183263/*!
    184 @brief  符号有り整数をprintfの%u相当の変換で文字列化する関数。
     264@brief  符号有り整数をprintfの%d(十進法表現)相当の変換で文字列化する関数。
    185265@author Egtra
    186266@date   2007/10/13
     
    198278        dwX = (-x) As DWord
    199279        signChar = Asc("-")
     280        flags Or= Sign
    200281    Else
    201282        dwX = x As DWord
     
    220301@param[in]  signChar    符号部分の文字。\0なら存在しないとして扱われる。
    221302@return xの文字列表現
     303
     304signCharに何らかの値を指定するときには、必ずflagsにSignまたはBlankを指定すること。
     305さもないと、フィールド幅を指定したときに正しく整形されなくなる。
    222306*/
    223307Function FormatInteger(x As DWord, d As DWord, field As DWord, flags As FormatFlags, signChar As StrChar) As String
     
    245329        .Append(buf, i + 1, len)
    246330
    247         AdjustFieldWidth(sb, field, signChar <> 0, flags)
     331        AdjustFieldWidth(sb, field, flags)
    248332    End With
    249333    FormatInteger = sb.ToString()
    250334End Function
    251335
    252 
    253 /*!
    254 DWordの最大値の八進法表現37777777777の文字数 - 1。FormatIntegerO内で使用。
    255 */
    256 Const MaxSizeO = 10
    257 
    258 /*!
    259 @brief  整数をprintfの%o相当の変換で文字列化する関数。
     336/*!
     337DWordの最大値の八進法表現37777777777の文字数 - 1 + 1。FormatIntegerO内で使用。
     338上の式で1を加えているのは、八進接頭辞の分。
     339*/
     340Const MaxSizeO = 11
     341
     342Dim TraitsIntegerO As IntegerConvertTraits
     343With TraitsIntegerO
     344    .Convert = AddressOf(IntegerO_Convert)
     345    .Prefix = AddressOf(IntegerO_Prefix)
     346    .MaxSize = MaxSizeO
     347End With
     348
     349/*!
     350@brief  符号無し整数をprintfの%o(八進法表現)相当の変換で文字列化する関数。
    260351@author Egtra
    261352@date   2007/10/19
     
    267358*/
    268359Function FormatIntegerO(x As DWord, d As DWord, field As DWord, flags As FormatFlags) As String
    269     PreProcessFormatInteger(d, flags)
    270 
    271     Dim sb = New System.Text.StringBuilder
    272     With sb
    273         Dim buf[MaxSizeO] As StrChar
    274         Dim i = MaxSizeO
    275         While x <> 0
    276             buf[i] = ((x And &o7) + &h30) As StrChar
    277             x >>= 3
    278             i--
    279         Wend
    280        
    281         Dim len = (MaxSizeO - i) As Long
    282 
    283         If flags And Alt Then
    284             .Append(&h30 As StrChar)
    285             len++
    286         End If
    287 
    288         If len < d Then
    289             .Append(&h30 As StrChar, d - len)
    290         End If
    291 
    292         .Append(buf, i + 1, len)
    293 
    294         AdjustFieldWidth(sb, field, False, flags And (Not (Sign Or Blank)))
    295     End With
    296     FormatIntegerO = sb.ToString()
     360    FormatIntegerO = FormatIntegerEx(TraitsIntegerO, x, d, field, flags)
     361End Function
     362
     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
     377
     378Function IntegerO_Prefix(x As QWord, flags As FormatFlags) As String
    297379End Function
    298380
     
    302384Const MaxSizeX = 7
    303385
    304 /*!
    305 @brief  整数をprintfの%x, %X相当の変換で文字列化する関数。
     386Dim TraitsIntegerX As IntegerConvertTraits
     387With TraitsIntegerX
     388    .Convert = AddressOf(IntegerX_Convert)
     389    .Prefix = AddressOf(IntegerX_Prefix)
     390    .MaxSize = MaxSizeX
     391End With
     392
     393/*!
     394@brief  整数をprintfの%x, %X(十六進法)相当の変換で文字列化する関数。
    306395@author Egtra
    307396@date   2007/10/19
     
    313402*/
    314403Function FormatIntegerX(x As DWord, d As DWord, field As DWord, flags As FormatFlags) As String
     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
    315443    PreProcessFormatInteger(d, flags)
    316444
    317445    Dim sb = New System.Text.StringBuilder
    318446    With sb
     447        Dim prefixFunc = tr.Prefix
     448        Dim prefix = prefixFunc(x, flags)
     449        sb.Append(prefix)
     450
    319451        Dim prefixLen = 0 As DWord
    320         If (flags And Alt) Then
    321             If x <> 0 Then
    322                 .Append("0X")
    323                 prefixLen = 2
    324             End If
    325         End If
    326 
    327         Dim buf[MaxSizeX] As StrChar
    328         Dim i = MaxSizeX
    329         While x <> 0
    330             buf[i] = _System_HexadecimalTable[x And &h0f]
    331             x >>= 4
    332             i--
    333         Wend
    334        
    335         Dim len = (MaxSizeX - i) As Long
    336 
     452        If String.IsNullOrEmpty(prefix) = False Then
     453            prefixLen = prefix.Length As DWord
     454        End If
     455
     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)
     459
     460        Dim len = (tr.MaxSize - bufStartPos) As Long
    337461        If len < d Then
    338462            .Append(&h30 As StrChar, d - len)
    339463        End If
    340464
    341         .Append(buf, i + 1, len)
    342 
    343         AdjustFieldWidth(sb, field, False, flags And (Not (Sign Or Blank)), prefixLen)
     465        .Append(buf, bufStartPos + 1, len)
     466
     467        AdjustFieldWidth(sb, field, flags And (Not (Sign Or Blank)), prefixLen)
    344468    End With
    345     FormatIntegerX = sb.ToString()
     469    FormatIntegerEx = sb.ToString()
    346470   
    347471    If (flags And Cap) = 0 Then
    348         FormatIntegerX = FormatIntegerX.ToLower()
     472        FormatIntegerEx = FormatIntegerEx.ToLower()
    349473    End If
    350474End Function
     
    365489    Else
    366490        '精度が指定されているとき、ゼロフラグは無視される。
    367         '左揃えのときも無視されるが、それはAdjustFieldWidthが行ってくれる。
     491        '仕様上、左揃えのときも無視されるが、それはAdjustFieldWidthが行ってくれる。
    368492        flags And= Not Zero
    369493    End If
     
    377501@param [in] flags   フラグ
    378502@param [in] prefixLen   (あれば)接頭辞の文字数。ゼロ埋めする際、この数だけ挿入位置を後ろにする。
    379 */
    380 Sub AdjustFieldWidth(sb As System.Text.StringBuilder, field As DWord, hasSign As Boolean, flags As FormatFlags, prefixLen = 0 As DWord)
     503sbが"-1"のように負符号を持っている場合は、呼出元でSignフラグ(またはBlank)を立てること。
     504*/
     505Sub AdjustFieldWidth(sb As System.Text.StringBuilder, field As DWord, flags As FormatFlags, prefixLen = 0 As DWord)
    381506    With sb
    382507        If .Length < field Then
     
    387512                Dim insPos As Long
    388513                If (flags And Zero) <> 0 Then
    389                     If ((flags And Blank) Or (flags And Sign) Or hasSign) Then
     514                    If (flags And Blank) Or (flags And Sign) Then
    390515                        insPos++
    391516                    End If
Note: See TracChangeset for help on using the changeset viewer.