Declare units to Convert from a string variable - delphi

I am using the Convert function within delphi to write a simple multi device application that will allow me to convert measurements.
The form consists of 2 Comboboxes and en edit. To select Conversion From, Conversion To and a value.
So in my comboboxes i select the units then i pass the value of the comboboxes to 2 global strings
fromType := cbConvFrom.Selected.Text;
toType := cbConvTo.Selected.Text;
Which return the following.
cbConvFrom returns duCentimeters
cbConvTo returns duMeters
However when i try to pass these values into a Convert function obviously they do not work as i am trying to pass a String as a TConvType, Shown below
conversion := Convert(StrToFloat(editValue.Text), fromType, toType);
How can i use these strings as a TConvType so the code above will work correctly.

Drop using string representation of conversion types and use descriptions instead. Get the descriptions from the registration of the type, using ConvTypeToDescription. Eg:
ComboBox1.Items.Add(ConvTypeToDescription(duMeters));
ComboBox1.Items.Add(ConvTypeToDescription(duCentimeters));
Then you can use the inverse to provide to the conversion function.
DescriptionToConvType(ComboBox1.Items[ComboBox1.ItemIndex], fromType);
DescriptionToConvType(ComboBox2.Items[ComboBox2.ItemIndex], toType);
conversion := Convert(StrToFloat(editValue.Text), fromType, toType);

Related

Constant single using the hex memory value in Delphi

How can I make a single constant based on a hex value where that hex value is an unsigned integer and the raw memory for the single. I would like to do something like this but it doesn't compile and this will try and cast the hex value to a single and then store the result of that cast instead of storing hex value itself:
LARGEST_SINGLE_LESS_THAN_ZERO = Single($80800000);
I get a "Invalid Typecast" error.
For example:
The single value for 1 is stored as $3F800000 in memory. I would like to be able to create a const that lets me set the value using $3F800000 instead of 1.
I have also tried other variations such as this without luck:
LARGEST_SINGLE_LESS_THAN_ZERO = PSingle(#$80800000)^;
Background
I have a method that I use to get the next smallest single when provided with a single value:
type
PInt32 = ^Int32;
function NextBefore(const aValue: Single): Single;
var
int32Value: Int32;
begin
// this function ignores special values nan/inf
int32Value := PInt32(#aValue)^;
if (UInt32(int32Value) = $80000000) or (int32Value = 0) then
begin
// special handling needed for -0 and 0. We need to go to the smallest
// negative number.
int32Value := $80800000;
end
else
begin
if int32Value >= 0 then
Dec(int32Value)
else
Inc(int32Value);
end;
Result := PSingle(#int32Value)^;
end;
This is really useful because we use vector operations that can only do a > or < so we use it to do the equivalent of a >= and a <=. We often check against 0. So where we need get all of the data >= 0 we do something like this:
MyVector.ThresholdGT(NextBefore(0));
It would be nicer to provide the other developers with a constant for these types of operations. Trying to use the PSingle format below won't work because the number is not a variable.
In order to declare a single constant with a hex value in such a way that it cannot be altered by code, it can be done in two steps:
const
iLARGEST_SINGLE_LESS_THAN_ZERO : Int32 = $80800000;
var
LARGEST_SINGLE_LESS_THAN_ZERO : Single absolute iLARGEST_SINGLE_LESS_THAN_ZERO;
Trying to change the value of LARGEST_SINGLE_LESS_THAN_ZERO will give a compiler error: Left side cannot be assigned to.
It's hard to do this cleanly with the constraints of the language. Perhaps the best that you can do is to make a variant record type that has both integer and single fields overlapped.
type
TSingleIntegerVariantRec = record
case Integer of
0: (I: Integer);
1: (S: Single);
end;
Once you have that type available you can declare typed constants using the integer field, but then read the single field.
const
LARGEST_SINGLE_LESS_THAN_ZERO: TSingleIntegerVariantRec = (I: $80800000);
....
MyVector.ThresholdGT(LARGEST_SINGLE_LESS_THAN_ZERO.S);
If you want to add an extra nuance you could implement an implicit cast operator to Single which would allow you to omit the .S. If you made that operator inline then I suspect the emitted code would be very efficient.
This does what you ask, but I wouldn't claim that it was very elegant. We're I you I would move the code to use the next value down into the library function so that you can pass 0 and shield the consumer of the library from these implementation details.
In other words you would add a ThresholdGTequal method that was implemented like this:
procedure TMyVector.ThresholdGTequal(const Value: Single);
begin
ThresholdGT(NextBefore(Value));
end;
Then the consumers of this code simply write:
MyVector.ThresholdGTequal(0);
and remain oblivious to all of the gnarly implementation details.

Convert hex str to decimal value in delphi

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;

delphi how to convert twitter timestamp to TDatetime

In continue to this question:
How to Convert Twitter Timestamp to DateTime?
what is the code to convert twitter date time stamp to TDateTime?
edit:
StrDateTime(const string;TFormatSettings);
could handle some of it,
now only to figure out how to intoduce new format.
Since we don't have the ParseExact function, you need to parse the components of the timestamp positionally. You could do it with the Copy() function. ex:
TheMonthAsString := Copy(TwitterDate,5,3);
TheDayAsString := Copy(TwitterDate,9,2);
etc..
Convert those pieces to Integers, and then you can use EncodeDateTime (in the DateUtils unit) (Thanks Jens!) to generate a TDateTime.
Summary: Pick the string apart into the individual components of the timestamp, and convert that to a TDateTime using EncodeDateTime or StrToDateTime.

Delphi - Write string to Reg_Binary registry key

I need to convert a string to write it into a registry.reg_binary key.
I have the basic code for writing into the key as follows:
try
rootkey := HKEY_CURRENT_USER;
if OpenKey(Key, False) then
begin
reg.WriteBinaryData('SomeKey', SomeValue, Length(SomeVale));
CloseKey;
end;
finally
reg.Free;
end;
In the above, SomeValue needs to be the hex value of a TEdit text field;
My current tack is convert the TEdit.text using IntToHex on the Ord value of each character. This gives me a string that looks like what I want to write...
At this point I'm stumped...
If you want to write a string, then you should call WriteString.
reg.WriteString('SomeKey', SomeValue);
If you have an integer, then call WriteInteger.
IntValue := StrToInt(SomeValue);
reg.WriteInteger('SomeKey', IntValue);
If you have true binary data, then it shouldn't matter what it looks like — hexadecimal or whatever. Call WriteBinaryData and be done with it. The actual appearance of the data is immaterial because you don't have to read it in that format. You'll read it later with ReadBinaryData and it will fill your buffer with the bytes in whatever format they had when you wrote them.
IntValue := StrToInt(SomeValue);
reg.WriteBinaryValue('SomeKey', IntValue, SizeOf(IntValue));
That will write all four bytes of your integer into the registry as a binary value.
When the Windows Registry Editor displays the key's value to you, it will probably display each byte in hexadecimal, but that's just a display format. It doesn't mean you have to format your data like that before you add it to the registry.
Granted this assumes that your string only contains ansi data,
but if your trying to write a string to a registry value as a binary value then the following changes to your logic would work:
var
EdStr : AnsiString;
:
EdStr := AnsiString(Edit1.Text); // de-unicode
reg.WriteBinaryData('SomeKey', EdStr[1], Length(EdStr));

Getting a Field List from a DBExpress TSQLQuery

I am having a problem getting a list of fields from a query defined at run time by the users of my program. I let my users enter a SQL query into a memo control and then I want to let them go through the fields that will return and do such things as format the output, sum column values and so forth. So, I have to get the column names so they have a place to enter the additional information.
I would do fine if there were no parameters, but I also have to let them define filter parameters for the query. So, if I want to set the parameters to null, I have to know what the parameter's datatype is.
I am using Delphi 2006. I connect to a Firebird 2.1 database using the DBExpress component TSQLConnection and TSQLQuery. Previously, I was successful using:
for i := 0 to Qry.Params.Count - 1 do Qry.Params[i].value := varNull;
I discovered I had a problem when I tried to use a date parameter. It was just a coincidence that all my parameters up until then had been integers (record IDs). It turns out that varNull is just an enumerated constant with a value of 1 so I was getting acceptable results (no records) was working okay.
I only need a list of the fields. Maybe I should just parse the SELECT clause of the SQL statement. I thought setting Qry.Prepared to True would get me a list of the fields but no such luck. It wants values for the parameters.
If you have an idea, I would sure like to hear it. Thanks for any help.
Replied again 'coz I'm interested. My methods works (with my queries) because they have been pre-defined with the params' datatypes preset to the correct type:)
I'm not sure how you are expecting the query to know or derive the datatype of the param given that you are not even selecting the field that it operates against.
So I think your query setup and user input method will need more attention. I've just looked up how I did this a while ago. I do not use a parameterised query - I just get the "parameter values" from the user and put them directly into the SQL. So your sql would then read:
SELECT s.hEmployee, e.sLastName
FROM PR_Paystub s
INNER JOIN PR_Employee e ON e.hKey = s.hEmployee
WHERE s.dtPaydate > '01/01/2008'
therefore no parameter type knowledge is necessary. Does not stop your users entering garbage but that goes back to input control :)
Although a slightly different dataset type this is what I use with TClientDataset simple and effective :)
for i := 0 to FilterDataSet.Params.Count -1 do
begin
Case FilterDataSet.Params.Items[i].Datatype of
ftString:
ftSmallint, ftInteger, ftWord:
ftFloat, ftCurrency, ftBCD:
ftDate:
ftTime:
ftDateTime:
.
.
.
end;
end;
can you not do something similar with the query?
You guys are making this way too hard:
for i := 0 to Qry.Params.Count - 1 do begin
Qry.Params[i].Clear;
Qry.Params[i].Bound := True;
end;
I'm not sure what version of Delphi you are using. In the Delphi 2006 help under Variant Types, it says:
Special conversion rules apply to the
Borland.Delphi.System.TDateTime type
declared in the System unit. When a
Borland.Delphi.System.TDateTime is
converted to any other type, it
treated as a normal Double. When an
integer, real, or Boolean is converted
to a Borland.Delphi.System.TDateTime,
it is first converted to a Double,
then read as a date-time value. When a
string is converted to a
Borland.Delphi.System.TDateTime, it is
interpreted as a date-time value using
the regional settings. When an
Unassigned value is converted to
Borland.Delphi.System.TDateTime, it is
treated like the real or integer value
0. Converting a Null value to Borland.Delphi.System.TDateTime raises
an exception.
The last sentence seems important to me. I would read that as varNull cannot be converted to a TDateTime to put into the field, and hence you get the exception that you're experiencing.
It also implies that this is the only special case.
Couldn't you do something like:
for i := 0 to Qry.Params.Count - 1 do
begin
if VarType(Qry.Params[i].value) and varTypeMask = varDate then
begin
Qry.Params[i].value := Now; //or whatever you choose as your default
end
else
begin
Qry.Params[i].value := varNull;
end;
end;
What I ended up doing was this:
sNull := 'NULL';
Qry.SQL.Add(sSQL);
for i := 0 to Qry.Params.Count - 1 do begin
sParamName := Qry.Params[i].Name;
sSQL := SearchAndReplace (sSQL, ':' + sParamName, sNull, DELIMITERS);
end;
I had to write SearchAndReplace but that was easy. Delimiters are just the characters that signal the end of a word.
TmpQuery.ParamByName('MyDateTimeParam').DataType := ftDate;
TmpQuery.ParamByName('MyDateTimeParam').Clear;
TmpQuery.ParamByName('MyDateTimeParam').Bound := True;

Resources