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?
Related
I'm trying to get Delphi to Round like Excel but I can't. Here is the code:
procedure TForm1.Button1Click(Sender: TObject);
var
s : string;
c : currency;
begin
c := 54321.245;
s := '';
s := s + Format('Variable: %m',[c]);
s := s + chr(13);
s := s + Format(' Literal: %m',[54321.245]);
ShowMessage(s);
end;
I'm using a currency variable that is set to 54321.245 and when I format this variable it rounds using Bankers Rounding. However, when I format the same value as a literal it rounds the way that Excel rounds.
I was expecting this to round to $54,321.25 whether it's formating a currency variable or a literal value. How can I make sure that Delphi rounds the same way as Excel every time?
Edit
The rounding I expect to see is as follows:
54,321.245 = 54,321.25
54,321.2449 = 54,321.24
54,431.2499 = 54,421.25
I am only using literals to show the different ways Delphi rounds. I expect to use variables in the actual code.
Note:
If I change the variable from currency to extended it rounds correctly
Edit #2
Some have suggested that I do not have a clear understanding of my requirements, this is absolutely not true. I have a very clear understanding of my requirements, I'm obviously not doing a very good job of explaining them. The rounding method I want is two decimal places. When the deimal part has a thousandths value >= 0.005 I want it rounded to 0.01 the currency type offered by Delphi does not do this. I also tried this example using Microsoft SQL with a money datatype (which I assumed was the same as Delphi's currency) and SQL rounds it's money type the way I described.
SQL Money >= 0.005 = 0.01
Delphi Currency >= 0.005 := 0.00
Edit #3
Good Article: http://rvelthuis.de/articles/articles-floats.html
Possible Solution: http://rvelthuis.de/programs/decimals.html
Edit #4
Here is one of the solutions from the Embarcadero discussion
function RoundCurrency(const Value: Currency): Currency;
var
V64: Int64 absolute Result;
Decimals: Integer;
begin
Result := Value;
Decimals := V64 mod 100;
Dec(V64, Decimals);
case Decimals of
-99 .. -50 : Dec(V64, 100);
50 .. 99 : Inc(V64, 100);
end;
end;
If I understand you correctly, you are looking for this:
function RoundTo2dp(Value: Currency): Currency;
begin
Result := Trunc(Value*100+IfThen(Value>0, 0.5, -0.5))/100;
end;
It's not possible to make RTL to round the way you want. The way to affect the rounding in Delphi is to use SetRoundMode which sets the FPU conrol word for rounding, however, as far as I can tell, there's no FPU support for rounding the exact in-between to upwards (which is generally avoided because it generates a bias for higher values).
You have to implement your own rounding function. There's an extended discussion in Delphi Rounding thread on Embarcadero forums, which includes several solutions.
use function System.Math.SimpleRoundTo
You can gain control on how delphi rounding numbers by :
uses Math;
...
procedure TForm1.Button1Click(Sender: TObject);
var
s : string;
c : currency;
begin
SetRoundMode(rmNearest);
c := 54321.245;
s := '';
s := s + Format('Variable: %m',[c]);
s := s + chr(13);
s := s + Format(' Literal: %m',[54321.245]);
ShowMessage(s);
end;
Unfortunately, using rmNearest, Delphi decides the number 54321.245 is closer to 54321.24 than 54321.25
With this code to retrieve the version of the installed MS Word:
uses uses oleauto;
[...]
function TForm2.GetWordVersion:string;
const
wdDoNotSaveChanges = 0;
var
WordApp: OLEVariant;
WordVersion: variant;
begin
Try
WordApp := CreateOLEObject('Word.Application');
WordVersion := WordApp.version;
WordApp.Quit(wdDoNotSaveChanges);
except
on E: Exception do
begin
WordVersion := -1;
end;
End;
Result := wordversion;
end;
I get 140 on my machine, my colleague gets 14. Both are win7/Word2010 but I am in Italy he is in India.
Anyone knows about this?
Why different values?
Thanks
I'm guessing this is a decimal separator issue. Word returns the string '14.0' and then when you convert to integer the period is treated as a positional separator on one machine, and a decimal separator on another.
The solution is to stop converting to integer which I infer that you are doing in code that you have not shown.
I am inferring that from this comment:
I can convert it to string and use the first 2 chars.
Since the code in the question operates on strings, I conclude that other code, not shown in the question, is converting to integer.
I'm trying to get Delphi to Round like Excel but I can't. Here is the code:
procedure TForm1.Button1Click(Sender: TObject);
var
s : string;
c : currency;
begin
c := 54321.245;
s := '';
s := s + Format('Variable: %m',[c]);
s := s + chr(13);
s := s + Format(' Literal: %m',[54321.245]);
ShowMessage(s);
end;
I'm using a currency variable that is set to 54321.245 and when I format this variable it rounds using Bankers Rounding. However, when I format the same value as a literal it rounds the way that Excel rounds.
I was expecting this to round to $54,321.25 whether it's formating a currency variable or a literal value. How can I make sure that Delphi rounds the same way as Excel every time?
Edit
The rounding I expect to see is as follows:
54,321.245 = 54,321.25
54,321.2449 = 54,321.24
54,431.2499 = 54,421.25
I am only using literals to show the different ways Delphi rounds. I expect to use variables in the actual code.
Note:
If I change the variable from currency to extended it rounds correctly
Edit #2
Some have suggested that I do not have a clear understanding of my requirements, this is absolutely not true. I have a very clear understanding of my requirements, I'm obviously not doing a very good job of explaining them. The rounding method I want is two decimal places. When the deimal part has a thousandths value >= 0.005 I want it rounded to 0.01 the currency type offered by Delphi does not do this. I also tried this example using Microsoft SQL with a money datatype (which I assumed was the same as Delphi's currency) and SQL rounds it's money type the way I described.
SQL Money >= 0.005 = 0.01
Delphi Currency >= 0.005 := 0.00
Edit #3
Good Article: http://rvelthuis.de/articles/articles-floats.html
Possible Solution: http://rvelthuis.de/programs/decimals.html
Edit #4
Here is one of the solutions from the Embarcadero discussion
function RoundCurrency(const Value: Currency): Currency;
var
V64: Int64 absolute Result;
Decimals: Integer;
begin
Result := Value;
Decimals := V64 mod 100;
Dec(V64, Decimals);
case Decimals of
-99 .. -50 : Dec(V64, 100);
50 .. 99 : Inc(V64, 100);
end;
end;
If I understand you correctly, you are looking for this:
function RoundTo2dp(Value: Currency): Currency;
begin
Result := Trunc(Value*100+IfThen(Value>0, 0.5, -0.5))/100;
end;
It's not possible to make RTL to round the way you want. The way to affect the rounding in Delphi is to use SetRoundMode which sets the FPU conrol word for rounding, however, as far as I can tell, there's no FPU support for rounding the exact in-between to upwards (which is generally avoided because it generates a bias for higher values).
You have to implement your own rounding function. There's an extended discussion in Delphi Rounding thread on Embarcadero forums, which includes several solutions.
use function System.Math.SimpleRoundTo
You can gain control on how delphi rounding numbers by :
uses Math;
...
procedure TForm1.Button1Click(Sender: TObject);
var
s : string;
c : currency;
begin
SetRoundMode(rmNearest);
c := 54321.245;
s := '';
s := s + Format('Variable: %m',[c]);
s := s + chr(13);
s := s + Format(' Literal: %m',[54321.245]);
ShowMessage(s);
end;
Unfortunately, using rmNearest, Delphi decides the number 54321.245 is closer to 54321.24 than 54321.25
I want to convert a large 64 bit value from decimal or hex string to 64 bit UINT64 data type. There is a UIntToStr to help converting the UINT64 to string, but no way to convert a 64 bit integer to a unsigned value, as a string. That means integer values greater than 2**63 can not be represented in decimal or hex, using the RTL. This is normally not a big deal, but it can happen that a user needs to input a value, as an unsigned integer, which must be stored into the registry as a 64 bit unsigned integer value.
procedure HandleLargeHexValue;
var
x:UINT64;
begin
x := $FFFFFFFFFFFFFFFE;
try
x := StrToInt('$FFFFFFFFFFFFFFFF'); // range error.
except
x := $FFFFFFFFFFFFFFFD;
end;
Caption := UintToStr(x);
end;
Update Val now works fine in Delphi XE4 and up. In XE3 and below Val('$FFFFFFFFFFFFFFFF') works but not Val('9223372036854775899'). As Roeland points out below in Quality Central 108740: System.Val had problems with big UInt64 values in decimal until Delphi XE4.
UPDATE: In XE4 and later the RTL bug was fixed. This hack is only useful in Delphi XE3 or older
Well, if it ain't there, I guess I could always write it.
(I wrote a pretty good unit test for this too, but its too big to post here)
unit UIntUtils;
{ A missing RTL function written by Warren Postma. }
interface
function TryStrToUINT64(StrValue:String; var uValue:UInt64 ):Boolean;
function StrToUINT64(Value:String):UInt64;
implementation
uses SysUtils,Character;
{$R-}
function TryStrToUINT64(StrValue:String; var uValue:UInt64 ):Boolean;
var
Start,Base,Digit:Integer;
n:Integer;
Nextvalue:UInt64;
begin
result := false;
Base := 10;
Start := 1;
StrValue := Trim(UpperCase(StrValue));
if StrValue='' then
exit;
if StrValue[1]='-' then
exit;
if StrValue[1]='$' then
begin
Base := 16;
Start := 2;
if Length(StrValue)>17 then // $+16 hex digits = max hex length.
exit;
end;
uValue := 0;
for n := Start to Length(StrValue) do
begin
if Character.IsDigit(StrValue[n]) then
Digit := Ord(StrValue[n])-Ord('0')
else if (Base=16) and (StrValue[n] >= 'A') and (StrValue[n] <= 'F') then
Digit := (Ord(StrValue[n])-Ord('A'))+10
else
exit;// invalid digit.
Nextvalue := (uValue*base)+digit;
if (Nextvalue<uValue) then
exit;
uValue := Nextvalue;
end;
result := true; // success.
end;
function StrToUINT64(Value:String):UInt64;
begin
if not TryStrToUINT64(Value,result) then
raise EConvertError.Create('Invalid uint64 value');
end;
end.
I must disagree that Val solves this issue.
Val works only for big UInt64 values when they are written in Hex. When they are written in decimal, the last character is removed from the string and the resulting value is wrong.
See Quality Central 108740: System.Val has problems with big UInt64 values
EDIT: It seems that this issue should be solved in XE4. Can't test this.
With Value a UINT64, the code snippet below gives the expected answer on Delphi 2010 but only if the input values are in hexadecimal
stringValue := '$FFFFFFFFFFFFFFFF';
val( stringValue, value, code );
ShowMessage( UIntToStr( value ));
I'd simply wrap val in a convenience function and you're done.
Now feel free to burn me. Am I missing a digit in my tests? :D
I'm trying to display a currency value in a grid, but I do not want the currency symbol to be shown:
if fPreferences.WorksheetFormat = 'Numeric' then
begin
CurrencyString := '';
Value := FieldByName('UnitList').AsCurrency;
end else
Value := CurrToStrF(FieldByName('UnitList').AsCurrency, ffCurrency, 2, langFormat);
The problem is that it's still showing the currency symbol. What am I doing wrong here? I don't think I can use CurrToStrF, because I need the grid to export a number to excel, not a string. Or, is there any way I can use AsFloat, but have to decimal places? (100.00)
Doing CurrencyString := ''; will impact all the following formatting of currencies when using the default format strings and thus should display all the currency variants/fields values without the $ sign, while retaining their numeric nature.
But when you explicitly format your currency value with your own TFormatSettings langFormat, it has no effect unless you previously did:
langFormat.CurrencyString := '';
Changing ffCurrency to ffFixed should get rid of the currency symbol but there wouldn't be any hundreds separators.
//With separators
sStrVar := FormatCurr('#,##0.00', CurrVar);
a very simple solution would be to change the CurrencyString yourself
and change it back to the original value later.
if fPreferences.WorksheetFormat = 'Numeric' then
begin
CurrencyString := '';
Value := FieldByName('UnitList').AsCurrency;
end else
begin
OldCurrStr := CurrencyString;
CurrencyString := '';
Value := CurrToStrF(FieldByName('UnitList').AsCurrency, ffCurrency, 2, langFormat);
CurrencyString := OldCurrStr;
end;