I've got a problem to convert a string representation of an hex value in integer value with Delphi.
for example:
$FC75B6A9D025CB16 give me 802829546 when i use the function:
Abs(StrToInt64('$FC75B6A9D025CB16'))
but if i use the calc program from Windows, the result is: 18191647110290852630
So my question is: who's right? me, or the calc?
Does anybody already have this kind of problem?
In fact 802829546 is clearly wrong here.
Calc returns a 64bit unsigned value (18191647110290852630d).
Delphi Int64 type uses highest bit as sign:
Int := StrToInt64('$FC75B6A9D025CB16');
Showmessage(IntToStr(Int));
returns value -255096963418698986 which is correct
If you need to work with values larger than 64bit signed, then check out Arnaud's answer here.
The number is too big to be represented as a signed 64-bit number.
FC75B6A9D025CB16h = 18191647110290852630d
The largest possible signed 64-bit value is
2^63 - 1 = 9223372036854775807
to work with big numbers you need external library for delphi
Large numbers in Pascal (Delphi)
I had to use a Delphi library named "DFF Library" because I work on Delphi6 and the type Uint64 does not exist in this version.
Main page
Here's my code to transform a string of hexadecimal value to a string of decimal value:
You need to add UBigIntsV3 to your uses in your unit.
function StrHexaToUInt64Str(const stringHexadecimal: String): string;
var
unBigInteger:TInteger;
begin
unBigInteger:=TInteger.Create;
try
// stringHexadecimal parameter is passed without the '$' symbol
// ex: stringHexadecimal:='FFAA0256' and not '$FFAA0256'
unBigInteger.AssignHex(stringHexadecimal);
//the boolean value determine if we want to add the thousand separator or not.
Result:=unBigInteger.converttoDecimalString(false);
finally
unBigInteger.free;
end;
end;
Related
The following code which attempts to convert a value well beyond the double precision range
StrToFloat('1e99999999')
correctly reports an incorrect floating point value in Delphi 10.2r3 with the Windows 32 bits compiler, but when compiled with the Window 64 bits compiler, it silently returns a 0 (zero).
Is there a way to have StrToFloat report an error when the floating point value is incorrect?
I have tried TArithmeticException.exOverflow, but this has no effect in that case.
I also tried TArithmeticException.exPrecision but it triggers in many usual approximation cases (f.i. it triggers when converting '1e9').
Issue was noticed with Delphi 10.2 update 3
addendum: to workaround the issue, I have started a clean-room alternative implementation of string to double conversion, initial version with tests can be found in dwscript commit 2ba1d4a
This is a defect that is present in all versions of Delphi that use the PUREPASCAL version of StrToFloat. That maps through to InternalTextToExtended which reads the exponent like this:
function ReadExponent: SmallInt;
var
LSign: SmallInt;
begin
LSign := ReadSign();
Result := 0;
while LCurrChar.IsDigit do
begin
Result := Result * 10;
Result := Result + Ord(LCurrChar) - Ord('0');
NextChar();
end;
if Result > CMaxExponent then
Result := CMaxExponent;
Result := Result * LSign;
end;
The problem is the location of
if Result > CMaxExponent then
This test is meant to be inside the loop, and in the asm x86 version of this code it is. As coded above, with the max exponent test outside the loop, the 16 bit signed integer result value is too small for your exponent of 99999999. As the exponent is read, the value in Result overflows, and becomes negative. So for your example it turns out that an exponent of -7937 is used rather than 99999999. Naturally this leads to a value of zero.
This is a clear bug and I have submitted a bug report: RSP-20333.
As for how to get around the problem, I'm not aware of another function in the Delphi RTL that performs this task. So I think you will need to do one of the following:
Roll your own StrToFloat.
Pre-process the string, and handle out of range exponents before they read StrToFloat.
Use one of the functions from the C runtime library that performs the same task.
Finally, I am grateful for you asking this question because I can see that my own program is affected by this defect and so I can now fix it!
Update:
You may also be interested to look at a related bug that I found when investigating: RSP-20334. It might surprise you to realise that, StrToFloat('߀'), when using the PUREPASCAL version of StrToFloat, returns 1936.0. The trick is that the character that is being passed to StrToFloat is a non-Latin digit, in this case U+07C0.
I'm comparing a SmallInt variable with the result of the Ord function.
Example:
var
MySmallInt : SmallInt;
begin
MySmallInt := 5;
if(MySmallInt > Ord('C'))
then ShowMessage('True')
else ShowMessage('False');
end
After doing this, the following warning message is shown (W1023):
W1023 Comparing signed and unsigned types - widened both operands
Delphi's hint on the Ord function says that it should return a SmallInt and that's why I can't understand what causes the warning message. (I've looked for Ord function in the System unit but I didn't find it).
Further Informations:
I'm testing under Delphi XE7.
Under Delphi 2007, the same code doesn't give me any warning.
As David said, Ord() is a so called "compiler magic" (or, as they call it now, "intrinsic" or "pseudo-") function, i.e. not a real function that is called, but just something that uses a function syntax, but is recognized by the compiler as a special construct and turned into code directly. The same is true for e.g. Chr(), Writeln(), etc. They can usually have different and/or multiple types of parameters or return values and sometimes even have additional syntax elements.
The documentation says, about Ord(X):
The result is the ordinal position of X; its type is the smallest standard integer type that can hold all values of X's type.
In Delphi XE7, 'C' is a WideChar, and the return value of Ord('C') will be a 16 bit unsigned type (Word). Smallint is signed type. That is why you get the warning, because you are comparing a signed and an unsigned type of the same size, so the values must be widened to the next larger type (Integer).
In Delphi 2007, 'C' is not a WideChar, it is an AnsiChar, so the result of Ord('C') is a Byte. There is no need for widening to the next larger type, since Smallint can contain all values of Byte, so both can be promoted to Smallint.
I agree that the info hint in the editor is deceptive. Ord() does not always return a Smallint, it returns the minimum type that is needed to hold all values of the argument.
Ord() is an intrinsic function that yields an unsigned type. Hence the warning. In your case, you are passing it a WideChar, and so the matching integral type is Word.
Please help me,
I know this may sound like very simple question, but i just can not figured it out how to make it work. I just started learning Unicode, so please give me some hint or example code.
I was converting my old encoding and decoding code from Delphi 5 to Delphi XE2. And when i call "Char" function it result in a different character, seem like it happen at the extended character of any encoding set.
At Delphi 5 :
Char(129) -> will result as empty char
At Delphi XE2 :
Char(129) -> will result #$81
I tried to used AnsiChar at delphi XE2, and the result is :
AnsiChar(129) -> will result as #129
What code should i used at delphi XE2, so it will return an empty char too. Not the #nn notation?
I need it to return the same result of Delphi 5, for the backward compatibility reason.
Is this have something to do with HIGHCHARUNICODE directive? I have read and tried it too, but still not luck.
Here the code that i tried at Delphi XE2, i make a simple one, but it did have a same logic with my encode / decode code. The code will get the char then put it into edit box.
procedure TForm1.Button1Click;
var
chars : Array[0..2] of AnsiChar;
ansi_string : AnsiString;
begin
chars[0] := AnsiChar(65);
chars[1] := AnsiChar(129);
chars[2] := AnsiChar(66);
ansi_string := chars;
// Here the ansi_string have a value of 'A'#$81'B'
EditBox1.Text := ansi_string;
// Here when i look the EditBox1.text in Evaluate/modify form,
// it shows 'A'#$0081'B'
// but at the form, it only show AB
end;
How can i make the ansi_string variable having a value of 'AB' instead of 'A'#$81'B'?
Thanks in Advance,
In Delphi 5, Char is AnsiChar, but in XE2 Char is WideChar instead. 127 is the highest signed value that an AnsiChar can hold, so a value of 129, which is hex $81, binary 10000001, would simply be interpreted as -127, which is also hex $81, binary 10000001. They are just different interpretations of the same bit value.
Depending on what your encoding/decoding code I actually doing, you will need to either use AnsiChar/AnsiString explicitly instead of Char/String generically, or switch to using Byte values, or else re-write the code to support Unicode properly and not make assumptions about the size of Char anymore. Hard to say since you did not show your actual code. But you should be OK with just using AnsiChar/AnsiString, since they do operate the same way they always have (the debugger may simply be displaying AnsiChar values differently, that's all).
In Delphi 7, int64s are signed, if I try to declare a hex constant larger than $8000000000000000 (eg, what is really an uint64) I get an error. Can you advise some workarounds, please?
You can make a variant record like so
type muint64 = record
case boolean of
true: (i64 : int64);
false:(lo32, hi32: cardinal);
end;
Now you can just use the cardinals to fill your uint64 with unsigned data.
The other option would be to use code like this:
const almostmaxint64 = $800000045000000;
var muint64: int64;
begin
muint64:= almostmaxint64;
muint64:= muint64 shl 1;
end
Without support from the compiler you don't have many options.
I'm presuming that you wish to pass a value to a function in some external DLL. You'll have to declare the parameter as a signed 64 bit integer, Int64. Then all you can do is pass in the signed value that has the same bit pattern as the desired unsigned value. Build yourself a little converter tool with a compiler that has support for unsigned 64 bit integers.
Traditionally, Broland implementations suffered interoperability issues because lack of largest unsigned supported by target platform. I remember using LongInt values instead of DWORD and waiting for troubles since very early days of Turbo Pascal for Windows. Then was Cardinal happiness, but no, D4 introduced largest integer Int64 in its signed form only. Again.
So your only option is to rely on signed fundamental type Int64 and pray... wait, no, just use Int64Rec typecast to perform arithmetics on least and most significant part separately.
Back to constant declaration:
const
foo = $8000004200000001; // this will work because hexadecimal notation is unsigned by its nature
// however, declared symbol foo becomes signed Int64 value
// attempting to use decimal numeral will result in "Integer constant too large" error
// see "True constants" topic in D7 Help for more details
procedure TForm1.FormCreate(Sender: TObject);
begin
// just to verify
Caption := IntToHex(foo, SizeOf(Int64) * 2);
end;
Unfortunately, the other workaround is to change your compiler. Free Pascal always keeps signed and unsigned types in sync.
This snippet compiles and yields correct result in Borland Delphi Version 15.0 (a.k.a Delphi 7).
I'm trying to figure out a way to check a string's first element if it's either a number or not.
if not(myString[0] in [0..9]) then //Do something
The problem is that I get an error "Element 0 inaccessible - use 'Length' or 'SetLength"
Another way came to my head from my C-like exprieince - convert the first element of the string to char and check the char,but there is no difference in the compile errors.
if not(char(myString[0]) in [0..9]) then //Do something
How do I accomplish it?
Strings are 1-based:
if not (myString[1] in ['0'..'9']) then // Do something
Pascal and Delphi indexes string from 1. This is a legacy from time where zero byte contained length, while next 255 (index 1 to 255) contained actual characters.
Joel Spolsky wrote quite good article on string issues:
http://www.joelonsoftware.com/articles/fog0000000319.html
Delphi strings use a 1-based index, so just rewrite to
if not(myString[1] in ['0'..'9']) then //Do something
Also take note of the quotes around the 0..9, otherwise you would be comparing characters to integers.
We should keep in mind some things:
String in Delphi is 0-based for mobile platforms and 1-based for Windows.
String in old versions of Delphi is AnsiString (1-byte per char) and WideString in new versions (2-bytes per char).
Delphi supports set of AnsiChar, but doesn't support set of WideChar.
So if we want to write a code compatible with all versions of Delphi, then it should be something like this:
if (myString[Low(myString)]>='0') and (myString[Low(myString)]<='9') then
// Do something
if not(myString[0] in [0..9]) then //Do something
If you're using Delphi 2009, the TCharacter class in Character.pas has functions like IsDigit to help simplify these kinds of operations.
Once you fix the indexing, of course. :)
With later updates to Delphi mobile code, the bottom string index changed from 0 to 1. When you compile older programmes, they compile and run correctly using 0 starting index. Programmes created with the later IDE produce an error. When you have mixtures, life gets complex!
It would be good to be able to take an older programme and tell the IDE that you want it brought up to date (maybe this would fix other things, like fonts getting scrambled when you answer a phone call!) but it would be good to get things consistent!
The simplest way to check to see if the first character of string is an integer, and then dispatch:
var
iResult : integer;
begin
if TryStrToInt( mySTring[1], iResult) then
begin
// handle number logic here iResult = number
end
else
begin
// handle non number logic here
end;
end;
I use a utility function to test the entire string:
function IsNumeric(const Value: string): Boolean;
var
i: Integer;
begin
Result := True;
for i := 1 to Length(Value) do
if not (Value[i] in ['0'..'9','.','+','-']) then
begin
Result := False;
Break;
end;
end;
The above code is for Delphi versions prior to 2007. In 2007 and 2009, you could change the integer variable i to a character c, and use for c in Value instead.
To test for integers only, remove the '.' from the set of characters to test against.
This is incorrect. ISO strings and older Pascal's also started at one. It is just a general convention, and afaik the s[0] thing is a result of that being vacant, and cheap to code in the UCSD bytecode interpreter. But that last bit is before my time, so only my guessing.
It results from the Pascal ability to have arbitrary upper and lower bounds, which provides for more typesafety accessing arrays.
Really old Pascal strings (till early eighties) strings were even worse than C ones btw. Multiple conventions were in used, but all were based on static arrays (like early C), but they were typically space padded, so you had scan back from the end till the spaces ended.
(removed the legacy tag, since being 1 based is not legacy. Accessing s[0] as length IS legacy, but that is not what the question is about)
Foreach element in strName
if not element in [0-9] then
do something
else
element is a digit
end if
Don't forget the quote between digits number.