System: Windows XP, Athalon X2 4800+, nVidia 8600GTS 256MB
Compiler/IDE: FPC 2.2.0
API: N/A (SysUtils/System?)

I've been using text files to represent data and read it in through the parser interface, but I've encountered a possible bug that is driving me nutty. Any float value converted from a string is completely bogus. For instance, take my logfile for an example, as I logged the value to convert and its conversion result.

Code:
BEGIN LOG -- 10/26/2007 9:12:31
Float encountered.  S = 0.9; V = 0.8999999762;
Float encountered.  S = 0.8; V = 0.8000000119;
END LOG -- 10/26/2007 9:12:35
I know those round to the values I want, it is a float to single assignment if that does anything, but the deviation makes me wonder. I'm using StrToFloat so if there's any problem with StrToFloat then it will affect any user of FreePascal 2.2.0 ... and yet I've seen no such bug reports.

Any ideas on what might've gone wrong on my system?

parser.exe (my experiment)
[pascal]program parser;

uses SysUtils, Classes, gDebug;

var AParser: TParser;
FS: TFileStream;

type
TArmorFlag = (afImpactResist, afSlashResist, afElecVuln);
TArmorFlags = set of TArmorFlag;
TBodyPart = (bpTorso, bpHead);

const
BodyParts: array[TBodyPart ] of string = ('torso','head');
ArmorFlags: array[TArmorFlag] of string = ('impact_resist','slash_resist','elec_vuln');

type
PArmor = ^TArmor;
TArmor = record
// NAME:
single, plural: string;
// DESC:
desc: string;
// VALUE, WEIGHT (OZ):
value, weight: word;
// MITIGATION (REDUCTION):
mitigation: single;
// BODYPART:
part: TBodyPart;
// FLAGS:
flags: TArmorFlags;
end;

var
Armory: array of TArmor;
Pos: Integer = -1;

Tok: Char;
tempS: string;
i: integer;

iP: TBodyPart;
aF: TArmorFlag;

BEGIN
writeln;
writeln('GameLib TParser Experiment');
FS := TFileStream.Create('armor.txt', fmOpenRead);
AParser := TParser.Create(FS);

writeln;
write('Generating objects.');

Tok := AParser.Token;
while Tok <> toEOF do begin
write('.');
tempS := AParser.TokenString;
if tempS = 'armor' then begin
Pos += 1;
SetLength(Armory, Pos+1);
end else if tempS = 'name' then begin
Tok := AParser.NextToken;
if Tok = toString then begin
Armory[Pos].single := AParser.TokenString;
end else
AParser.CheckToken(toString);
end else if tempS = 'plural' then begin
Tok := AParser.NextToken;
if Tok = toString then begin
Armory[Pos].plural := AParser.TokenString;
end else
AParser.CheckToken(toString);
end else if tempS = 'desc' then begin
Tok := AParser.NextToken;
if Tok = toString then begin
Armory[Pos].desc := AParser.TokenString;
end else
AParser.CheckToken(toString);
end else if tempS = 'value' then begin
Tok := AParser.NextToken;
if Tok = toInteger then begin
Armory[Pos].value := AParser.TokenInt;
end else
AParser.CheckToken(toInteger);
end else if tempS = 'weight' then begin
Tok := AParser.NextToken;
if Tok = toInteger then begin
Armory[Pos].weight := AParser.TokenInt;
end else
AParser.CheckToken(toInteger);
end else if tempS = 'mitigation' then begin
Tok := AParser.NextToken;
if Tok = toFloat then begin
Armory[Pos].mitigation := StrToFloat(AParser.TokenString);
Log('Float encountered. S = '+AParser.TokenString+'; V = '+FloatToStr(Armory[Pos].mitigation)+';');
end else
AParser.CheckToken(toFloat);
end else if tempS = 'bodypart' then begin
Tok := AParser.NextToken;
if Tok = toSymbol then begin
tempS := AParser.TokenString;
for iP := low(TBodyPart) to high(TBodyPart) do
if tempS = BodyParts[iP] then begin
Armory[Pos].part := iP;
break;
end;
end else
AParser.CheckToken(toSymbol);
end else if tempS = 'flags' then begin
Tok := AParser.NextToken;
while Tok = toSymbol do begin
tempS := AParser.TokenString;
if tempS = 'done' then break;
for aF := low(TArmorFlag) to high(TArmorFlag) do
if ArmorFlags[aF] = tempS then
include(Armory[pos].flags, aF);
Tok := AParser.NextToken;
end;
end else
AParser.Error('Unrecognized object/token: '+tempS);
Tok := AParser.NextToken;
end;
writeln(' Completed.');

for i := low(Armory) to high(Armory) do begin
writeln('Armor #',i+1,' {');
writeln('Singular: ', Armory[i].single);
writeln('Plural: ', Armory[i].plural);
writeln('Description: ', Armory[i].desc);
writeln('Value: $', Armory[i].value);
writeln('Weight: ', Armory[i].weight);
writeln('Mitigation: ', FloatToStr(Armory[i].mitigation));
writeln('Bodypart: ', BodyParts[Armory[i].part]);
write ('Flags: ');
for aF := low(TArmorFlag) to high(TArmorFlag) do
if aF in Armory[Pos].flags then
write(ArmorFlags[aF],' ');
writeln;
writeln('}');
writeln;
end;

readln;
AParser.Free;
FS.Free;
END.[/pascal]
armor.txt (the datafile)
Code:
armor
name		'bronzium plate'
plural		'bronzium plating'
desc		'The key behind bronzium is the polymer chain, of bronze itself, that transforms ordinary bronze into a lattice-like structure that is exponentially tougher.  This change is costly and takes great skill, but produces the greatest of armors able to resist even the finest steel.'
value		25000
weight		208
mitigation	0.9
bodypart	torso
flags		impact_resist slash_resist elec_vuln
done

armor
name		'bronzium helmet'
plural		'bronzium helmets'
desc		'The key behind bronzium is the polymer chain, of bronze itself, that transforms ordinary bronze into a lattice-like structure that is exponentially tougher.  This change is costly and takes great skill, but produces the greatest of armors able to resist even the finest steel.'
value		17500
weight		80
mitigation	0.8
bodypart	head
flags		slash_resist elec_vuln
done