I have such a table in SQLite :
CREATE TABLE [TABLE1] (
[T_ID] INTEGER PRIMARY KEY ON CONFLICT IGNORE AUTOINCREMENT,
[DATE] DATE UNIQUE ON CONFLICT IGNORE,
[FIELD1] INTEGER,
[FIELD2] INTEGER,
[FIELD3] INTEGER,
[FIELD4] REAL);
I added a 2 new calculated fields to the TABLE1
called SUM1 (integer) and SUM2 (float).
procedure TForm3.UniTable1CalcFields(DataSet: TDataSet);
begin
UNITable1.Fields.FieldByName('SUM1').asInteger := (UNITable1.Fields.FieldByName('FIELD1').AsInteger) + (UNITable1.Fields.FieldByName('FIELD2').AsInteger) + (UNITable1.Fields.FieldByName('FIELD3').AsInteger);
UNITable1.Fields.FieldByName('SUM2').asFloat := (UNITable1.Fields.FieldByName('SUM1').AsInteger) / (UNITable1.Fields.FieldByName('FIELD4').AsInteger) ;
end;
This kind of works but I am having trouble vwith SUM2 displaying a 15 digit number.
I would like to display a number with only two decimals like 5,81.
I could do it in the cxGrid by setting the properties of the field to CalcEdit and set Precision to 3.
But I am wondering can this be done in code.
Is there a way I can accomplish this ?
Well 314 / 54 is 5.814814814814... . If you are using persistent fields you can set the DisplayFormat to ',0.00;; '
or in code:
TNumericField(UNITable1.Fields.FieldByName('SUM2')).DisplayFormat := ',0.00;; ';
Another option is to use the FormatFloat('0.00',myvariable) function.
'0.00' defines the format (2 decimal places), and myvariable is an aptly named variable.
The only drawbacks are that it requires an extra few lines of code to declare and assign a value to the variable, and if you wish to use this value for further calculations, the accuracy will be lower than the original floating point number you had.
procedure TForm3.UniTable1CalcFields(DataSet: TDataSet);
var
var1: real;
begin
//Assign the floating point value to the variable
var1 := (UNITable1.Fields.FieldByName('SUM1').AsInteger) / (UNITable1.Fields.FieldByName('FIELD4').AsInteger);
UNITable1.Fields.FieldByName('SUM1').asInteger := UNITable1.Fields.FieldByName('FIELD1').AsInteger) + (UNITable1.Fields.FieldByName('FIELD2').AsInteger) + (UNITable1.Fields.FieldByName('FIELD3').AsInteger);
UNITable1.Fields.FieldByName('SUM2').AsFloat := FormatFloat('0.00',var1);
end;
i haven't had a chance to test this, but the basic idea would be something along these lines.
Related
I'm working with Delphi and Assembly, so, i had a problem. I used a instruction(RDTSC) in Assembly of getting a 64-bits read time-stamp, the instruction put the numbers separately in two registers EAX and EDX. But it's ok, i get it with Delphi Integer variables. But now, i need to join those variables in 1 of 64-bits. It's like:
Var1 = 46523
var2 = 1236
So i need to put it into one variable like:
Var3 = 465231236
it's like a StrCat, but i'm don't know how to do it. Somebody can help me?
You certainly don't want to concatenate the decimal string representations of the two values. That is not the way you are expected to combine the two 32 bit values returned from RTDSC into a 64 bit value.
Combining 46523 and 1236 should not yield 465231236. That is the wrong answer. Instead, you want to take the high order 32 bits, and put them alongside the low order 32 bits.
You are combining $0000B5BB and $00004D4. The correct answer is either $0000B5BB00004D4 or $00004D40000B5BB, depending on which of the two values are the high and low order parts.
Implement this in code, for instance, using Int64Rec:
var
Value: UInt64;
...
Int64Rec(Value).Lo := Lo;
Int64Rec(Value).Hi := Hi;
where Lo and Hi are the low and high 32 bit values returned by RTDSC.
So, bits 0 to 31 are set to the value of Lo, and bits 32 to 63 are set to the value of Hi.
Or it can be written using bitwise operations:
Value := (UInt64(Hi) shl 32) or UInt64(Lo);
If all you need to do is read the time stamp counter, then you don't need to do any of this though. You can implement the function like this:
function TimeStampCounter: UInt64;
asm
RDTSC
end;
The register calling convention requires that a 64 bit value return value is passed back to the caller in EDX:EAX. Since the RDTSC places the values in those exact registers (not a coincidence by the way), you have nothing more to do.
All of this said, rather than using the time stamp counter, it is usually preferable to use the performance counter, which is wrapped by TStopWatch from System.Diagnostics.
The simple way is to use a record
type
TMyTimestamp = record
case Boolean of
true:
( Value: Int64 );
false:
( Value1: Integer; Value2: Integer );
end;
and you can store/read each value as you like
var
ts: TMyTimestamp;
begin
ts.Value1 := 46523;
ts.Value2 := 1236;
WriteLn( ts.Value ); // -> 5308579624379
ts.Value := 5308579624379;
WriteLn( ts.Value1 ); // -> 46523
WriteLn( ts.Value2 ); // -> 1236
end;
see: Docwiki: Variant Parts in Records
procedure TForm1.Button1Click(Sender: TObject);
var
xlap,xlbook,xlsheet:variant;
y:integer;
begin
xlap:=createoleobject('excel.application');
xlbook:=xlap.workbooks.add;
xlap.visible:=true;
xlsheet:=xlbook.worksheets.add;
for y:=1 to 50 do
begin
xlsheet.cells[y,2].value:= concat('=IF(A',y,'>6,"Good","Bad")')
inc(y);
end;
end;
That's my code to make an Excel through Delphi. I want to input the IF formula into column B of the Excel, but there is error when I use the concat('=IF(A',y,'>6,"Good","Bad")').
May be I need another way to include y between those strings.
Any suggestion? Thanks before
Delphi has a format statement bit like sprintf printf in c, well nearly
xlsheet.cells[y,2].value:= format('=IF(A%d>6,"Good", "Bad")',[y])
%d is a place holder for an int. Look it up for loads of other stuff.
NB the variables you want to interpolate are assed in an array.
In addition to Tony's answer about Format, here are a couple of other approaches. (Format is great if you have mixed types or many values, but it carries some overhead that might not be needed.)
You can concatenate strings with a simple + - in fact, the Concat documentation says it does the same thing but is faster:
Temp := 'ing';
Str := 'Test' + Temp; // Str = 'Testing'
As your y variable is an integer, you'll need to convert it to a string first (note you don't need to Inc(y);, as the loop will do that already - it's automatically incremented from the starting value to the ending value on each pass through the loop):
for y:=1 to 50 do
begin
xlsheet.cells[y,2].value:= '=IF(A' + IntToStr(y) + '>6,"Good","Bad")';
end;
We are working in Delphi 2006 with devexpress.
We have a cxGrid.
We want to restrict the entry of values for a number column, integers between 0 and 999.
If I set the property type to a SpinEdit the initial value is always 0 which is unwanted.
So I left the property value on the column null and set the datatype on the databinding of the column to Smallint. That works for the most part but an 'e' and '.' and '+' and '-' can still be entered into the column which causes exceptions.
Is some simple way to exclude the 'e' and '.' and '+' and '-' from being entered into the column?
The initial 0 Value can be prevented by setting UseNullString to true.
The input of unwanted characters can handled by
procedure TForm1.ViewEditKeyPress(Sender: TcxCustomGridTableView; AItem: TcxCustomGridTableItem;
AEdit: TcxCustomEdit; var Key: Char);
begin
if AItem = TheColumnWithSpinEdit then
if (not (Key in ['0'..'9',#8])) then Key := #0;
end;
I am currently having problems with screating a scoreboard in Delphi.
I have a series of forms which are individual questions.
If the questions are answered correctly, then the score is 1. Otherwise the score is -1.
On my scoreboard at the moment, I have 12 labels and 11 of them contain the score for each of the forms.
What I would like to do is add up the numbers in each of the labels and output the final score into the 12th label.
Is there a way of doing this?
Any help will be greatly appreciated.
You should use the UI purely for displaying your values.
For working with your data you should use appropriate data structures: array, lists, etc.
Example using an Array:
var
Scores[0..10]: Integer;
Sum: Integer;
procedure CollectData;
var
i: Integer;
begin
Scores[0] := ...;
//...
Scores[10] := ...;
Sum := 0;
for i := Low(Scores) to High(Scores) do
Sum := Sum + Scores[i];
end;
procedure DisplayData;
begin
Label1.Caption := IntToStr(Scores[0]);
//...
Label11.Caption := IntToStr(Scores[10]);
Label12.Caption := IntToStr(Sum);
end;
Neat solution: Keep scores as integers in Integer fields
Not so neat solution:
SumLabel.Caption := IntToStr( StrToIntDef( Label1.Caption, 0 ) + StrToIntDef( Label2.Caption, 0 ) + ... );
Although I think #DR's answer is spot-on, and #Ritsaert's is helpful, here's another option.
Your label components will have a 'TAG' property - you can use this for your own purposes and in your case I'd just set the TAG property at the same time as you set the Caption.
The advantage behind this is that you can format your caption to contain more than a simple number (if you wish), and also you are just summing up tags (which are already integers and don't need you to do the extra work behind a StrToIntDef call). Really, you're following #DR's point about keeping values out of the GUI (in a sense), you're using a storage field in each label instead.
eg;
when setting a score;-
Label1.Caption:=Format('%d point',[FScore]);
Label1.Tag:=FScore;
and when summing them;-
FSum:=Label1.Tag + Label2.Tag + Label3.Tag (etc)
I'm working with Delphi 2009,I binged my question,but the answers I've gotten are outdated since It doesn't recognise StrtoFloat in Delphi2009.
I'm asking how to convert an integer ,for example, '1900000' to '1,900,000'?
You can also use the format command. Because the format expects a real number, adding 0.0 to the integer effectively turns it into an extended type.
Result := Format('%.0m',[intValue + 0.0]));
This handles negative numbers properly and adds the currency symbol for the users locale. If the currency symbol is not wanted, then set CurrencyString := ''; before the call, and restore it afterwards.
SavedCurrency := CurrencyString;
try
CurrencyString := '';
Result := Format('%.0m',[intValue + 0.0]));
finally
CurrencyString := SavedCurrency;
end;
To force commas, just set the ThousandSeparator := ',';
CurrencyString := '!';
ThousandSeparator := '*';
Result := Format('%.0m',[-1900000.0]);
// Returns (!1*900*000) in my locale.
The "period" in the mask determines how the fractional portion of the float will display. Since I passed 0 afterwards, it is telling the format command to not include any fractional pieces. a format command of Format('%.3m',[4.0]) would return $4.000.
I currently use this :
function FloatToCurrency(const f: double): string;
begin
Result := FormatFloat('#,###.##;1;0', f);
end;
It doesn't work with negative numbers, but since you need currency you won't have that problem.
You can assign Integer to Currency directly by assignment, the compiler will do the conversion for you:
var
Int : Integer;
Cur : Currency;
begin
Int := 1900000;
Cur := Int;
ShowMessage(CurrToStr(Cur)); // 1900000
ShowMessage(Format('%m', [Cur]); // 1,900,000.00 in US/UK/NZ/AU etc, "1 900 000,00" in Spain etc.
ShowMessage(Format('%.0m', [Cur]); // 1,900,000 in US/UK/NZ/AU etc, "1 900 000" in Spain etc.
end;
If you want Commas using Spanish regional settings set ThousandSeparator := ','; or use the extended CurrToStrF(amount, ffCurrency, decimals, FormatSettings)) version.
The verison with FormatSettings is also thread-safe.
Note: You can't assign Currency to Integer directly, You would need to use Int := Trunc(Cur) but this is inefficient as it converts to float first (unless compiler does something smart).
wouldnt this be more of a format thing, delphi should have some type of support for formating the number into a string the way you want right? Besides isnt the newer versions of delphi more aligned with the .net framework?