123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- function IntFloatToTextFmt(Buf: FPChar; const Value; ValueType: TFloatValue; Format: FPChar; const AFormatSettings: TFormatSettings): Integer;
- Type
- TPosArray = Array[0..3] of Integer;
- const
- MaxPrecision = 18; // Extended precision
- var
- // Input in usable format
- E : Extended; // Value as extended.
- FV: TFloatRec; // Value as floatrec.
- Section : String; // Format can contain 3 sections, semicolon separated: Pos;Neg;Zero. This is the one to use.
- SectionLength : Integer; // Length of section.
- // Calculated based on section. Static during output
- ThousandSep: Boolean; // Thousands separator detected in format ?
- IsScientific: Boolean; // Use Scientific notation ? (E detected in format)
- DecimalPos: Integer; // Position of decimal point in pattern.
- FirstDigit: Integer; // First actual digit in input (# or 0), relative to decimal point
- LastDigit: Integer; // Last required (0) digit, relative to decimal point
- RequestedDigits: Integer; // Number of requested digits, # and 0 alike
- ExpSize : Integer; // Number of digits in exponent
- Available: Integer; // Available digits in FV.
- // These change during output loop
- Current: Integer; // Current digit in available digits
- PadZeroes: Integer; // Difference in requested digits before comma and exponent, needs to be padded with zeroes.
- DistToDecimal: Integer; // Place of current digit, relative to decimal point taking in account PadZeroes!
- Procedure InitVars;
- begin
- if ValueType = fvCurrency then
- E:=Currency(Value)
- else
- E:=Extended(Value);
- Section:='';
- SectionLength:=0;
- ThousandSep:=false;
- IsScientific:=false;
- DecimalPos:=0;
- FirstDigit:=MaxInt;
- LastDigit:=0;
- RequestedDigits:=0;
- ExpSize:=0;
- Available:=-1;
- end;
- procedure ToResult(const AChar: FChar); inline;
- begin
- Buf[Result]:=AChar;
- Inc(Result);
- // Writeln('->',AChar,'(',Ord(AChar),') : ',Result);
- end;
- procedure AddToResult(const AStr: FString);
- var
- I : Integer;
- begin
- For I:=1 to Length(AStr) do
- ToResult(AStr[I]);
- end;
- procedure WriteDigit(ADigit: FChar);
- // Write a digit to result, prepend with decimalseparator or append with 1000 separator
- begin
- if ADigit=#0 then exit;
- // Writeln('WriteDigit: ',ADigit,', DistToDecimal: ',DistToDecimal);
- Dec(DistToDecimal);
- // -1 -> we've arrived behind the decimal
- if (DistToDecimal=-1) then
- begin
- ToResult(AFormatSettings.DecimalSeparator);
- ToResult(ADigit);
- end
- else
- begin
- // We're still before the decimal.
- ToResult(ADigit);
- if ThousandSep and ((DistToDecimal mod 3)=0) and (DistToDecimal>1) then
- ToResult(AFormatSettings.ThousandSeparator);
- end;
- end;
- Function GetDigit : FChar;
- // Return next digit from available digits.
- // May return #0 if none available.
- // Will return '0' if applicable.
- begin
- // Writeln(' DistToDecimal <= LastDigit : ',DistToDecimal,' < ',LastDigit,' have digit: ',Current<=Available, '(',Current,')');
- Result:=#0;
- if (Current<=Available) then
- begin
- Result:=FV.Digits[Current];
- Inc(Current);
- end
- else if (DistToDecimal <= LastDigit) then
- Dec(DistToDecimal)
- else
- Result:='0';
- // Writeln('GetDigit ->: ',Result);
- end;
- procedure CopyDigit;
- // Copy a digit (#, 0) to the output with the correct value
- begin
- // Writeln('CopyDigit ');
- if (PadZeroes=0) then
- WriteDigit(GetDigit) // No shift needed, just copy what is available.
- else if (PadZeroes<0) then
- begin
- // We must prepend zeroes
- Inc(PadZeroes);
- if (DistToDecimal<=FirstDigit) then
- WriteDigit('0')
- else
- Dec(DistToDecimal);
- end
- else
- begin
- // We must append zeroes
- while PadZeroes > 0 do
- begin
- WriteDigit(GetDigit);
- Dec(PadZeroes);
- end;
- WriteDigit(GetDigit);
- end;
- end;
- Function GetSections(Var SP : TPosArray) : Integer;
- var
- FL : Integer;
- i : Integer;
- C,Q : FChar;
- inQuote : Boolean;
- begin
- Result:=1;
- SP[1]:=-1;
- SP[2]:=-1;
- SP[3]:=-1;
- inQuote:=False;
- Q:=#0;
- I:=0;
- FL:=StrLen(Format);
- while (I<FL) do
- begin
- C:=Format[I];
- case C of
- ';':
- begin
- if not InQuote then
- begin
- if Result>3 then
- Raise Exception.Create('Invalid float format');
- SP[Result]:=I+1;
- Inc(Result);
- end;
- end;
- '"','''':
- begin
- if InQuote then
- InQuote:=C<>Q
- else
- begin
- InQuote:=True;
- Q:=C;
- end;
- end;
- end;
- Inc(I);
- end;
- if SP[Result]=-1 then
- SP[Result]:=FL+1;
- end;
- Procedure AnalyzeFormat;
- var
- I,Len: Integer;
- Q,C: FChar;
- InQuote : Boolean;
- begin
- Len:=Length(Section);
- I:=1;
- InQuote:=False;
- Q:=#0;
- while (I<=Len) do
- begin
- C:=Section[i];
- if C in ['"',''''] then
- begin
- if InQuote then
- InQuote:=C<>Q
- else
- begin
- InQuote:=True;
- Q:=C;
- end;
- end
- else if not InQuote then
- case C of
- '.':
- if (DecimalPos=0) then
- DecimalPos:=RequestedDigits+1;
- ',':
- ThousandSep:=AFormatSettings.ThousandSeparator<>#0;
- 'e', 'E':
- begin
- Inc(I);
- if (I<Len) then
- begin
- C:=Section[i];
- IsScientific:=C in ['-','+'];
- if IsScientific then
- while (I<Len) and (Section[i+1]='0') do
- begin
- Inc(ExpSize);
- Inc(I);
- end;
- if ExpSize>4 then
- ExpSize:=4;
- end;
- end;
- '#':
- Inc(RequestedDigits);
- '0':
- begin
- if RequestedDigits<FirstDigit then
- FirstDigit:=RequestedDigits+1;
- Inc(RequestedDigits);
- LastDigit:=RequestedDigits+1;
- end;
- end;
- Inc(I);
- end;
- if DecimalPos=0 then
- DecimalPos:=RequestedDigits+1;
- // Writeln('LastDigit: ',DecimalPos,'-',LastDigit);
- LastDigit:=DecimalPos-LastDigit;
- if LastDigit>0 then
- LastDigit:=0;
- // Writeln('FirstDigit: ',DecimalPos,'-',FirstDigit);
- FirstDigit:=DecimalPos-FirstDigit;
- if FirstDigit<0 then
- FirstDigit:=0;
- end;
- Function ValueOutSideScope : Boolean;
- begin
- With FV do
- Result:=((Exponent >= 18) and (not IsScientific)) or (Exponent = $7FF) or (Exponent = $800)
- end;
- Procedure CalcRunVars;
- Var
- D,P: Integer;
- begin
- if IsScientific then
- begin
- P:=RequestedDigits;
- D:=9999;
- end
- else
- begin
- P:=MaxPrecision;
- D:=RequestedDigits-DecimalPos+1;
- end;
- FloatToDecimal(FV,Value,ValueType,P,D);
- DistToDecimal:=DecimalPos-1;
- if IsScientific then
- PadZeroes:=0 // No padding.
- else
- begin
- PadZeroes:=FV.Exponent-(DecimalPos-1);
- if (PadZeroes>=0) then
- DistToDecimal:=FV.Exponent
- end;
- // Writeln('PadZeroes : ',PadZeroes, ', DistToDecimal : ',DistToDecimal);
- Available:=-1;
- while (Available<High(FV.Digits)) and (FV.Digits[Available+1]<>#0) do
- Inc(Available);
- // Writeln('Available: ',Available);
- end;
- Function FormatExponent(ASign: FChar; aExponent: Integer) : FString;
- begin
- if E = 0 then
- aExponent := 0;
- Result:=IntToStr(Abs(aExponent));
- Result:=StringOfChar('0',ExpSize-Length(Result))+Result;
- if (aExponent<0) then
- Result:='-'+Result
- else if (aExponent>=0) and (aSign='+') then
- Result:=aSign+Result;
- end;
- var
- I,S : Integer;
- C,Q : FChar;
- PA : TPosArray;
- InLiteral : Boolean;
- begin
- Result:=0;
- Initvars;
- // What section to use ?
- if (E>0) then
- S:=1
- else if (E<0) then
- S:=2
- else
- S:=3;
- PA[0]:=0;
- I:=GetSections(PA);
- if (I<S) or (PA[S]-PA[S-1]=0) then
- S:=1;
- // Extract correct section
- SectionLength:=PA[S]-PA[S-1]-1;
- SetLength(Section,SectionLength);
- Move(Format[PA[S-1]],Section[1],SizeOf(FChar)*SectionLength);
- // Writeln('Section ',I,' : "',Section,'" ',SectionLength);
- AnalyzeFormat;
- // Writeln('RequestedDigits: ',RequestedDigits,', DecimalPos : ',DecimalPos,', LastDigit: ',LastDigit,', FirstDigit: ',FirstDigit);
- CalcRunVars;
- // If we cannot process value using current settings, fallback
- if (SectionLength=0) or ValueOutSideScope then
- Exit(FloatToText(FPChar(Buf), E, ffGeneral, 15, 0, AFormatSettings));
- // Get Started
- I:=1;
- Current:=0;
- Q:=' ';
- InLiteral:=False;
- if (FV.Negative) and (S=1) then
- ToResult('-');
- while (I<=SectionLength) do
- begin
- C:=Section[i];
- // Writeln('Analyzing pos ',I,': "',C,'"');
- If (C in ['"', '''']) then
- begin
- if InLiteral then
- InLiteral:=C<>Q
- else
- begin
- inLiteral:=True;
- Q:=C;
- end;
- end
- else if InLiteral then
- ToResult(C)
- else
- case C of
- '0', '#':
- CopyDigit;
- '.', ',':
- ; // Do nothing, handled by CopyDigit
- 'e', 'E':
- begin
- ToResult(C); // Always needed
- if IsScientific then
- begin
- Inc(I);
- if I<=Section.Length then
- begin
- C:=Section[I];
- if (C in ['+','-']) then
- begin
- AddToResult(FormatExponent(C,FV.Exponent-DecimalPos+1));
- // Skip rest
- while (I<SectionLength) and (Section[i+1]='0') do
- Inc(I);
- end;
- end;
- end
- else if I< SectionLength Then
- begin
- inc(I);
- ToResult(Section[i]);
- end;
- end;
- else
- ToResult(C);
- end;
- Inc(i);
- end;
- // Writeln('Result ',Result);
- end;
|