delphi中formatFloat代码初探

来源:互联网 发布:帝国cms绑定二级域名 编辑:程序博客网 时间:2024/09/21 11:13

   由于项目需要,需要在qt下实现floatformat的函数。之前写过一个,但是写得不好。决定重新写一个,参考delphi xe2下的实现。把xe2下的相关代码都看了一遍,xe2的代码思路在这里贴出来。xe2下的代码在system.sysutils下,核心函数是InternalFloatToTextFmt。它有汇编的版本跟纯pascal的版本。汇编看不懂,参考的是纯pascal的版本。

  整体流程是:

1)解析format格式化字符串

 Result := 0;  DecimalSep := AFormatSettings.DecimalSeparator; //根据系统取得小数点的符号  ThousandsSep := AFormatSettings.ThousandSeparator; //根据系统取得千分位的符号。这两个最好也通过系统API来取,如果要做国际化的话,这两个在不同的系统上是不一样的。  if ValueType = fvCurrency then    Temp := Currency(Value)  else    Temp := Extended(Value);  if Extended(Temp) > 0 then    AIndex := 0  else    if Extended(Temp) < 0 then      AIndex := 1    else      AIndex := 2;  SectionIndex := FindSection(AIndex);  Section := ScanSection(SectionIndex);  //这个主要是取得format的格式字符串,取得小数点,千分位的位置,还有是否显示科学计数法,取得填充0的位置。
这段代码主要是分析格式化字符串。这个可以直接参考代码翻译成C++的。

2)将浮点数转成十进制的数字,用的函数是 FloatToDecimal(FloatValue, Value, ValueType, Precision, Digits);

由于Delphi中的extended的类型是10字节的,C++没有对应数据类型,而且它的内存结构比较复杂。这个函数就自己实现了。它的功能就是把float的类型数据存入到TFloatRec的结构体中。
  TFloatRec = packed record    Exponent: Smallint;  //表示几位整数(100,对应的3)    Negative: Boolean; //表示是否为负数    Digits: array[0..20] of AnsiChar;//将浮点数中的数字取出来存到里面,如果是0.XXX的,第一个0不存。  end;
C++的实现代码如下:

    result->negative = value < 0;    if (fabs(value) < 1e-16)    {        result->exponent = 0;        result->digits[0] = '\0';        return;    }    if (result->negative)    {        value = abs(value);    }    GString strDigits = GString::number(value, 'f', 18);    result->exponent = strDigits.indexOf("."); //算出小数点前的位数    int nStrIndex = 0;    if (value < 1) //小数前的那个0不算    {        result->exponent = 0;        nStrIndex = 1;    }    int nIndex = 0;    while (true) //把数字压入floatRec中    {        if (strDigits[nStrIndex] == QChar('.'))        {            nStrIndex++;            continue;        }        if (nStrIndex > 19 || (nStrIndex >= strDigits.length()))        {            break;        }        result->digits[nIndex] = strDigits[nStrIndex].toLatin1();        nStrIndex++;        nIndex++;    }    if (result->exponent + decimals < 0)    {        result->exponent = 0;        result->negative = false;        result->digits[0] = 0;        return;    }
这段代码执行后,会根据小数位数进行截断,还有进行四舍五入。

  while true do      begin        Result.Digits[J] := #0;        Dec(J);        if J < 0 then        begin          Result.Digits[0] := '1';          Inc(Result.Exponent);          Exit;        end;        Inc(Result.Digits[J]);        if Result.Digits[J] <= '9' then          Exit;      end;

3)对之前处理的TFloatRec的进行格式化处理 。
  if (FormatLength = 0) or (GetCharIndex(Format, 0) = ';') or    ((FloatValue.Exponent >= 18) and (not Scientific)) or    (FloatValue.Exponent = $7FF) or (FloatValue.Exponent = $800) then    if Unicode then      Result := FloatToText(PWideChar(Buf), Value, ValueType, ffGeneral, 15, 0)    else      Result := FloatToText(PAnsiChar(Buf), Value, ValueType, ffGeneral, 15, 0)//这个可以直接调QSTring::num()来实现,将浮点数转成字符串  else    ApplyFormat;//这个函数来实现格式化,可以直接翻译成C++
applyFormat的函数主要是根据那个格式化字符串,取出TFloatRec的digits里的数字拼成一个字符串出来。

整体来说思路就是这样。

0 0
原创粉丝点击