mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-12 04:26:13 +02:00
* let FloatToStr output the correct number of decimals in case the
first significant digit is preceded by several zeroes (patch by C. Western, mantis #16907) git-svn-id: trunk@19738 -
This commit is contained in:
parent
e2b5ba756d
commit
a4804a3c25
@ -1161,8 +1161,8 @@ const
|
|||||||
|
|
||||||
Function FloatToStrFIntl(const Value; format: TFloatFormat; Precision, Digits: Integer; ValueType: TFloatValue; Const FormatSettings: TFormatSettings): String;
|
Function FloatToStrFIntl(const Value; format: TFloatFormat; Precision, Digits: Integer; ValueType: TFloatValue; Const FormatSettings: TFormatSettings): String;
|
||||||
Var
|
Var
|
||||||
P: Integer;
|
P, PE, Q, Exponent: Integer;
|
||||||
Negative, TooSmall, TooLarge: Boolean;
|
Negative: Boolean;
|
||||||
DS: Char;
|
DS: Char;
|
||||||
|
|
||||||
function RemoveLeadingNegativeSign(var AValue: String): Boolean;
|
function RemoveLeadingNegativeSign(var AValue: String): Boolean;
|
||||||
@ -1197,82 +1197,102 @@ Begin
|
|||||||
Begin
|
Begin
|
||||||
case ValueType of
|
case ValueType of
|
||||||
fvCurrency:
|
fvCurrency:
|
||||||
begin
|
|
||||||
If (Precision = -1) Or (Precision > 19) Then Precision := 19;
|
If (Precision = -1) Or (Precision > 19) Then Precision := 19;
|
||||||
TooSmall:=False;
|
|
||||||
end;
|
|
||||||
else
|
else
|
||||||
begin
|
|
||||||
If (Precision = -1) Or (Precision > maxdigits) Then Precision := maxdigits;
|
If (Precision = -1) Or (Precision > maxdigits) Then Precision := maxdigits;
|
||||||
TooSmall := (Abs(Extended(Value)) < 0.00001) and (Extended(Value)<>0.0);
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
If Not TooSmall Then
|
{ First convert to scientific format, with correct precision }
|
||||||
Begin
|
case ValueType of
|
||||||
case ValueType of
|
fvDouble:
|
||||||
fvDouble:
|
Str(Double(Extended(Value)):precision+7, Result);
|
||||||
Str(Double(Extended(Value)):0:precision, Result);
|
fvSingle:
|
||||||
fvSingle:
|
Str(Single(Extended(Value)):precision+6, Result);
|
||||||
Str(Single(Extended(Value)):0:precision, Result);
|
fvCurrency:
|
||||||
fvCurrency:
|
|
||||||
{$ifdef FPC_HAS_STR_CURRENCY}
|
{$ifdef FPC_HAS_STR_CURRENCY}
|
||||||
Str(Currency(Value):0:precision, Result);
|
Str(Currency(Value):precision+6, Result);
|
||||||
{$else}
|
{$else}
|
||||||
Str(Extended(Currency(Value)):0:precision, Result);
|
Str(Extended(Currency(Value)):precision+8, Result);
|
||||||
{$endif FPC_HAS_STR_CURRENCY}
|
{$endif FPC_HAS_STR_CURRENCY}
|
||||||
else
|
else
|
||||||
Str(Extended(Value):0:precision, Result);
|
Str(Extended(Value):precision+8, Result);
|
||||||
|
end;
|
||||||
|
{ Delete leading spaces }
|
||||||
|
while Result[1] = ' ' do
|
||||||
|
System.Delete(Result, 1, 1);
|
||||||
|
P := Pos('.', Result);
|
||||||
|
if P<>0 then
|
||||||
|
Result[P] := DS
|
||||||
|
else
|
||||||
|
Exit; { NAN or other special case }
|
||||||
|
{ Consider removing exponent }
|
||||||
|
PE:=Pos('E',Result);
|
||||||
|
if PE > 0 then begin
|
||||||
|
{ Read exponent }
|
||||||
|
Q := PE+2;
|
||||||
|
Exponent := 0;
|
||||||
|
while (Q <= Length(Result)) do begin
|
||||||
|
Exponent := Exponent*10 + Ord(Result[Q])-Ord('0');
|
||||||
|
Inc(Q);
|
||||||
end;
|
end;
|
||||||
Negative := Result[1] = '-';
|
if Result[PE+1] = '-' then
|
||||||
P := Pos('.', Result);
|
Exponent := -Exponent;
|
||||||
if P<>0 then
|
if (P+Exponent < PE) and (Exponent > -6) then begin
|
||||||
Result[P] := DS;
|
{ OK to remove exponent }
|
||||||
TooLarge :=(P > Precision + ord(Negative) + 1) or (Pos('E', Result)<>0);
|
SetLength(Result,PE-1); { Trim exponent }
|
||||||
End;
|
if Exponent >= 0 then begin
|
||||||
|
{ Shift point to right }
|
||||||
If TooSmall Or TooLarge Then
|
for Q := 0 to Exponent-1 do begin
|
||||||
begin
|
Result[P] := Result[P+1];
|
||||||
Result := FloatToStrFIntl(Value, ffExponent, Precision, Digits, ValueType,FormatSettings);
|
Inc(P);
|
||||||
// Strip unneeded zeroes.
|
|
||||||
P:=Pos('E',result)-1;
|
|
||||||
If P<>-1 then
|
|
||||||
begin
|
|
||||||
{ delete superfluous +? }
|
|
||||||
if result[p+2]='+' then
|
|
||||||
system.Delete(Result,P+2,1);
|
|
||||||
While (P>1) and (Result[P]='0') do
|
|
||||||
begin
|
|
||||||
system.Delete(Result,P,1);
|
|
||||||
Dec(P);
|
|
||||||
end;
|
|
||||||
If (P>0) and (Result[P]=DS) Then
|
|
||||||
begin
|
|
||||||
system.Delete(Result,P,1);
|
|
||||||
Dec(P);
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
end
|
Result[P] := DS;
|
||||||
else if (P<>0) then // we have a decimalseparator
|
P := 1;
|
||||||
begin
|
if Result[P] = '-' then
|
||||||
{ it seems that in this unit "precision" must mean "number of }
|
Inc(P);
|
||||||
{ significant digits" rather than "number of digits after the }
|
while (Result[P] = '0') and (P < Length(Result)) and (Result[P+1] <> DS) do
|
||||||
{ decimal point" (as it does in the system unit) -> adjust }
|
{ Trim leading zeros; conversion above should not give any, but occasionally does
|
||||||
{ (precision+1 to count the decimal point character) }
|
because of rounding }
|
||||||
{ don't just cut off the string, as rounding must be taken }
|
System.Delete(Result,P,1);
|
||||||
{ into account based on the final digit }
|
end else begin
|
||||||
|
{ Add zeros at start }
|
||||||
if (Length(Result) > Precision + ord(Negative) + 1) and
|
Insert(Copy('00000',1,-Exponent),Result,P-1);
|
||||||
(Precision + ord(Negative) + 1 >= P) then
|
Result[P-Exponent] := Result[P-Exponent-1]; { Copy leading digit }
|
||||||
Result := FloatToStrFIntl(Value, ffFixed,
|
Result[P] := DS;
|
||||||
0, Precision - (P - Ord(Negative) - 1),
|
if Exponent <> -1 then
|
||||||
ValueType, FormatSettings);
|
Result[P-Exponent-1] := '0';
|
||||||
P := Length(Result);
|
end;
|
||||||
While (P>0) and (Result[P] = '0') Do
|
{ Remove trailing zeros }
|
||||||
Dec(P);
|
Q := Length(Result);
|
||||||
If (P>0) and (Result[P]=DS) Then
|
while (Q > 0) and (Result[Q] = '0') do
|
||||||
Dec(P);
|
Dec(Q);
|
||||||
SetLength(Result, P);
|
if Result[Q] = DS then
|
||||||
|
Dec(Q); { Remove trailing decimal point }
|
||||||
|
if (Q = 0) or ((Q=1) and (Result[1] = '-')) then
|
||||||
|
Result := '0'
|
||||||
|
else
|
||||||
|
SetLength(Result,Q);
|
||||||
|
end else begin
|
||||||
|
{ Need exponent, but remove superfluous characters }
|
||||||
|
{ Delete trailing zeros }
|
||||||
|
while Result[PE-1] = '0' do begin
|
||||||
|
System.Delete(Result,PE-1,1);
|
||||||
|
Dec(PE);
|
||||||
|
end;
|
||||||
|
{ If number ends in decimal point, remove it }
|
||||||
|
if Result[PE-1] = DS then begin
|
||||||
|
System.Delete(Result,PE-1,1);
|
||||||
|
Dec(PE);
|
||||||
|
end;
|
||||||
|
{ delete superfluous + in exponent }
|
||||||
|
if Result[PE+1]='+' then
|
||||||
|
System.Delete(Result,PE+1,1)
|
||||||
|
else
|
||||||
|
Inc(PE);
|
||||||
|
while Result[PE+1] = '0' do
|
||||||
|
{ Delete leading zeros in exponent }
|
||||||
|
System.Delete(Result,PE+1,1)
|
||||||
end;
|
end;
|
||||||
|
end;
|
||||||
End;
|
End;
|
||||||
|
|
||||||
ffExponent:
|
ffExponent:
|
||||||
|
@ -9,6 +9,49 @@ const
|
|||||||
var
|
var
|
||||||
ErrCount: longint;
|
ErrCount: longint;
|
||||||
|
|
||||||
|
procedure CheckVal(f: Extended);
|
||||||
|
var
|
||||||
|
s: string;
|
||||||
|
f1: Extended;
|
||||||
|
begin
|
||||||
|
s := FloatToStr(f);
|
||||||
|
f1 := StrToFloat(s);
|
||||||
|
if (f<>f1) and (Abs(f-f1)/Abs(f) > 6e-15) then begin
|
||||||
|
WriteLn('Error (Double):',Abs(f-f1)/Abs(f), ' Input:', f, ' Output:', s);
|
||||||
|
Inc(ErrCount);
|
||||||
|
end;
|
||||||
|
f := Single(f);
|
||||||
|
s := FloatToStr(Single(f));
|
||||||
|
f1 := StrToFloat(s);
|
||||||
|
if (f<>f1) and (Abs(f-f1)/Abs(f) > 6e-10) then begin
|
||||||
|
WriteLn('Error (Single):',Abs(f-f1)/Abs(f), ' Input:', f, ' Output:', s);
|
||||||
|
Inc(ErrCount);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure Cycle(f: Extended);
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
for i := 1 to 50 do begin
|
||||||
|
CheckVal(f);
|
||||||
|
CheckVal(-f);
|
||||||
|
f := f/10;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure CycleInc(f, increment: Extended);
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
Cycle(f);
|
||||||
|
for i := 0 to 30 do begin
|
||||||
|
Cycle(f+increment);
|
||||||
|
Cycle(f-increment);
|
||||||
|
increment := increment/10;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure CheckResult(const s, ref: string);
|
procedure CheckResult(const s, ref: string);
|
||||||
begin
|
begin
|
||||||
if s <> ref then
|
if s <> ref then
|
||||||
@ -24,6 +67,8 @@ var
|
|||||||
d: double;
|
d: double;
|
||||||
s: single;
|
s: single;
|
||||||
c: currency;
|
c: currency;
|
||||||
|
i: Integer;
|
||||||
|
tests: array [0..4] of Double = (123456789123456789., 1e20, 1.6e20, 5e20, 9e20);
|
||||||
begin
|
begin
|
||||||
e:=1234567890123.4;
|
e:=1234567890123.4;
|
||||||
d:=12345.12345;
|
d:=12345.12345;
|
||||||
@ -46,6 +91,14 @@ begin
|
|||||||
NegCurrFormat:=8;
|
NegCurrFormat:=8;
|
||||||
CheckResult(FloatToStrF(-12345.1234,ffCurrency,19,4), '-12' + ThousandSeparator + '345'+DecimalSeparator+'1234 ' + CurrencyString);
|
CheckResult(FloatToStrF(-12345.1234,ffCurrency,19,4), '-12' + ThousandSeparator + '345'+DecimalSeparator+'1234 ' + CurrencyString);
|
||||||
CheckResult(FloatToStrF(MinCurrency,ffCurrency,19,4), '-922' + ThousandSeparator + '337' + ThousandSeparator + '203' + ThousandSeparator + '685' + ThousandSeparator + '477'+DecimalSeparator+'5807 ' + CurrencyString);
|
CheckResult(FloatToStrF(MinCurrency,ffCurrency,19,4), '-922' + ThousandSeparator + '337' + ThousandSeparator + '203' + ThousandSeparator + '685' + ThousandSeparator + '477'+DecimalSeparator+'5807 ' + CurrencyString);
|
||||||
|
for i := 0 to High(tests) do begin
|
||||||
|
e := tests[i];
|
||||||
|
CycleInc(e,1e20);
|
||||||
|
CycleInc(e,9e20);
|
||||||
|
CycleInc(e,e);
|
||||||
|
CycleInc(e,e/2);
|
||||||
|
CycleInc(e,e/3);
|
||||||
|
end;
|
||||||
if ErrCount > 0 then
|
if ErrCount > 0 then
|
||||||
begin
|
begin
|
||||||
writeln('Test failed. Errors: ', ErrCount);
|
writeln('Test failed. Errors: ', ErrCount);
|
||||||
|
Loading…
Reference in New Issue
Block a user