Title: String or Number? Undocumented Effects of Val(...)
Question: Recently I ran across an interesting phenomenon where an obvious string was converted into a number by Delphis Functions: Val, IntToStr and InToStrDef.
Answer:
Hi folks,
since quite some time I am using the following function to check whether a submitted string is a number or "just" a string with some non-numeric characters.
function IsInteger(const Str: String): Boolean;
var
E, I: Integer;
begin
Val(Str, I, E);
Result := E = 0;
end;
It worked fine, or so it seemed, until the other day a rather rare situation occered.
The function returned True on the value of "x56e", which wasn't quite the thing I expected to happen. After a while of searching for the reason it became rather obvious. Delphi tries to interpret strings starting with either "0x", "x", or "$" as hexa-decimal numbers.
Well for strings starting with "$" I knew it would, in fact I wrote an articel about it, but the fact that "0x" and "x" are interpreted as well, was a newie for me.
Therefore "x56e" is equal to "0x56e", which is equal to "$056E" which is "Delphi"-hexadecimal to 1390 (decimal).
Anyway, I looked into the source code of the Val function, and here is the place where I found the solution:
@@endBlanks:
MOV CH,0
CMP BL,'-'
JE @@minus
CMP BL,'+'
JE @@plus
CMP BL,'$'
JE @@dollar
--- CMP BL, 'x'
JE @@dollar
--- CMP BL, 'X'
JE @@dollar
--- CMP BL, '0'
JNE @@firstDigit
MOV BL, [ESI]
INC ESI
--- CMP BL, 'x'
JE @@dollar
--- CMP BL, 'X'
JE @@dollar
TEST BL, BL
JE @@endDigits
JMP @@digLoop
The new version of my function converts the string to a number and back - if incoming string and resulting string are equal, it was a number, just the way I was it expecting to work.
function IsInteger(const Str: String): Boolean;
begin
Result := IntToStr(StrToIntDef(Str, -1)) = Str;
end;
THANKS MAGNUS - THERE IS STILL A BUG IN THE PREVIOUS SOLUTION - SEE COMMENTS FOR A SOLUTION THAT ALMOST WORKS - you did forget about empty strings, though ;-)
Therefore, I will allow myself to add your solution to the article, slightly editied. Thanks again!
function IsInteger(Str: String) : boolean;
var
I, L: Integer
begin
Result := False;
L := Length(Str);
if L = 0 then
// empty string
Exit;
if L 1 then
// first character for strings longer than one char
if not (Str[1] in ['+', '-', '0'..'9']) then
Exit
else
else
// first character for strings exact one char long
if not (Str[1] in ['0'..'9']) then
Exit;
for I := 2 to length(Str) do
// check remaining characters
if not (Str[I] in ['0'..'9']) then
Exit;
Result := True;
end;
I hope, this will help you a little bit, once you are going to check for a numeric value.
By the way, no book I know of pays respect to this effect, a nice on it is, though ;-)
Content Ace