由于项目需要,需要在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);
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;
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++