How to convert a floating point number to a string with max. 2 decimal digits in Delphi - delphi

How can I convert a floating point number to a string with a maximum of 2 decimal digits in Delphi7?
I've tried using:
FloatToStrF(Query.FieldByName('Quantity').AsFloat, ffGeneral, 18, 2, FS);
But with the above, sometimes more than 2 decimal digits are given back, ie. the result is: 15,60000009

Use ffFixed instead of ffGeneral.
ffGeneral ignores the Decimal parameter.
When you use ffGeneral, the 18 is saying that you want 18 significant decimal digits. The routine will then express that number in the shortest manner, using scientific notation if necessary. The 2 is ignored.
When you use ffFixed, you are saying you want 2 digits after the decimal point.
If you are wondering about why you sometimes get values that seem to be imprecise, there is much to be found on this site and others that will explain how floating-point numbers work.
In this case, AsFloat is returning a double, which like (most) other floating-point formats, stores its value in binary. In the same way that 1/3 cannot be written in decimal with finite digits, neither can 15.6 be represented in binary in a finite number of bits. The system chooses the closest possible value that can be stored in a double. The exact value, in decimal, is:
15.5999999999999996447286321199499070644378662109375
If you had asked for 16 digits of precision, the value would've been rounded off to 15.6. But you asked for 18 digits, so you get 15.5999999999999996.

If you really mean what you write (MAX 2 decimal digits) and does not mean ALWAYS 2 decimal digits, then the two code snippets in the comments won't give you want you asked for (they will return a string that ALWAYS has two decimal digits, ie. ONE is returned as "1.00" (or "1,00" for Format depending on your decimal point).
If you truly want an option with MAX 2 decimal digits, you'll have to do a little post-processing of the returned string.
FUNCTION FloatToStrMaxDecimals(F : Extended ; MaxDecimals : BYTE) : STRING;
BEGIN
Result:=Format('%.'+IntToStr(MaxDecimals)+'f',[F]);
WHILE Result[LENGTH(Result)]='0' DO DELETE(Result,LENGTH(Result),1);
IF Result[LENGTH(Result)] IN ['.',','] THEN DELETE(Result,LENGTH(Result),1)
END;
An alternative (and probably faster) implementation could be:
FUNCTION FloatToStrMaxDecimals(F : Extended ; MaxDecimals : BYTE) : STRING;
BEGIN
Result:=Format('%.'+IntToStr(MaxDecimals)+'f',[F]);
WHILE Result[LENGTH(Result)]='0' DO SetLength(Result,PRED(LENGTH(Result)));
IF Result[LENGTH(Result)] IN ['.',','] THEN SetLength(Result,PRED(LENGTH(Result)))
END;
This function will return a floating point number with MAX the number of specified decimal digits, ie. one half with MAX 2 digits will return "0.5" and one third with MAX 2 decimal digits will return "0.33" and two thirds with MAX 2 decimal digits will return "0.67". TEN with MAX 2 decimal digits will return "10".
The final IF statement should really test for the proper decimal point, but I don't think any value other than period or comma is possible, and if one of these are left as the last character in the string after having stripped all zeroes from the end, then it MUST be a decimal point.
Also note, that this code assumes that strings are indexed with 1 for the first character, as it always is in Delphi 7. If you need this code for the mobile compilers in newer Delphi versions, you'll need to update the code. I'll leave that exercise up to the reader :-).

i use this function in my application:
function sclCurrencyND(Const F: Currency; GlobalDegit: word = 2): Currency;
var R: Real; Fact: Currency;
begin
Fact:= power(10, GlobalDegit);
Result:= int(F*Fact)/Fact;
end;

Related

Why multiply two double in dart result in very strange number

Can anyone explain why the result is 252.99999999999997 and not 253? What should be used instead to get 253?
double x = 2.11;
double y = 0.42;
print(((x + y) * 100)); // print 252.99999999999997
I am basically trying to convert a currency value with 2 decimal (ie £2.11) into pence/cent (ie 211p)
Thanks
In short: Because many fractional double values are not precise, and adding imprecise values can give even more imprecise results. That's an inherent property of IEEE-754 floating point numbers, which is what Dart (and most other languages and the CPUs running them) are working with.
Neither of the rational numbers 2.11 and 0.42 are precisely representable as a double value. When you write 2.11 as source code, the meaning of that is the actual double values that is closest to the mathematical number 2.11.
The value of 2.11 is precisely 2.109999999999999875655021241982467472553253173828125.
The value of 0.42 is precisely 0.419999999999999984456877655247808434069156646728515625.
As you can see, both are slightly smaller than the value you intended.
Then you add those two values, which gives the precise double result 2.529999999999999804600747665972448885440826416015625. This loses a few of the last digits of the 0.42 to rounding, and since both were already smaller than 2.11 and 0.42, the result is now even more smaller than 2.53.
Finally you multiply that by 100, which gives the precise result 252.999999999999971578290569595992565155029296875.
This is different from the double value 253.0.
The double.toString method doesn't return a string of the exact value, but it does return different strings for different values, and since the value is different from 253.0, it must return a different string. It then returns a string of the shortest number which is still closer to the result than to the next adjacent double value, and that is the string you see.

Binary to Decimal in Delphi 7

I'm trying to do a simple console program where user input a binary string and he get a decimal number. I dont need to check if the binary string have something else than 0 or 1. I already managed to do decimal to binary but can't do it the other way.
I tried some code found on SO and Reddit but most of the time i got error I/O 105
Here's the dec to bin :
program dectobin;
{$APPTYPE CONSOLE}
uses
SysUtils,
Crt32;
var
d,a :Integer;
str :String;
begin
str:='';
Readln(a);
while a>0 do begin
d:=a mod 2;
str:=concat(IntToStr(d),str);
a:=a div 2;
end;
Writeln(str);
Readln;
end.```
Basics of positional number systems (PNS)
The number of unique digits in a PNS is its base (or radix)
The decimal system (base = 10) has ten digits, 0..9
The binary system (base = 2) has two digits, 0..1
The position of a digit in a number determines its weight:
the rightmost digit has a weight of units (1), (base)^0 (decimal 1, binary 1)
the second (from right) digit has a weight of (base)^1 (decimal 10, binary 2)
the third (from right) digit has a weight of (base)^2 (decimal 100, binary 4)
and so on ....
Note that the weight is always base * weight of previous digit (in a right to left direction)
General interpretation of a string of numbers in any positional number system
assign a variable 'result' = 0
assign a variable 'weight' = 1 (for (base)^0 )
repeat
read the rightmost (not yet read) digit from string
convert digit from character to integer
multiply it by weight and add to variable 'result'
multiply weight by base in prep. for next digit
until no more digits
After previous, you can use e.g. IntToStr() to convert to decimal string.

sscanf in flex changing value of input

I'm using flex and bison to read in a file that has text but also floating point numbers. Everything seems to be working fine, except that I've noticed that it sometimes changes the values of the numbers. For example,
-4.036 is (sometimes) becoming -4.0359998, and
-3.92 is (sometimes) becoming -3.9200001
The .l file is using the lines
static float fvalue ;
sscanf(specctra_dsn_file_yytext, "%f", &fvalue) ;
The values pass through the yacc parser and arrive at my own .cpp file as floats with the values described. Not all of the values are changed, and even the same value is changed in some occurrences, and unchanged in others.
Please let me know if I should add more information.
float cannot represent every number. It is typically 32-bit and so is limited to at most 232 different numbers. -4.036 and -3.92 are not in that set on your platform.
<float> is typically encoded using IEEE 754 single-precision binary floating-point format: binary32 and rarely encodes fractional decimal values exactly. When assigning values like "-3.92", the actual values saved will be one close to that, but maybe not exact. IOWs, the conversion of -3.92 to float was not exact had it been done by assignment or sscanf().
float x1 = -3.92;
// float has an exact value of -3.9200000762939453125
// View # 6 significant digits -3.92000
// OP reported -3.9200001
float x2 = -4.036;
// float has an exact value of -4.035999774932861328125
// View # 6 significant digits -4.03600
// OP reported -4.0359998
Printing these values to beyond a certain number of significant decimal digits (typically 6 for float) can be expected to not match the original assignment. See Printf width specifier to maintain precision of floating-point value for a deeper C post.
OP could lower expectations of how many digits will match. Alternatively could use double and then only see this problem when typically more than 15 significant decimal digits are viewed.

double rounding

I this is code that rounds 62 to 61 and shows it in the output. Why it decides to round and how to get 62 in the output?
var d: double;
i: integer;
begin
d:=0.62;
i:= trunc(d*100);
Showmessage( inttostr(i) );
end;
This boils down to the fact that 0.62 is not exactly representable in a binary floating point data type. The closest representable double value to 0.62 is:
0.61999 99999 99999 99555 91079 01499 37383 83054 73327 63671 875
When you multiply this value by 100, the resulting value is slightly less that 62. What happens next depends on how the intermediate value d*100 is treated. In your program, under the 32 bit Windows compiler with default settings, the intermediate value is held in an 80 bit extended register. And the closest 80 bit extended precision value is:
61.99999 99999 99999 55591 07901 49937 38383 05473 32763 67187 5
Since the value is less than 62, Trunc returns 61 since Trunc rounds towards zero.
If you stored d*100 in a double value, then you'd see a different result.
d := 0.62;
d := d*100;
i := Trunc(d);
Writeln(i);
This program outputs 62 rather than 61. That's because although d*100 to extended 80 bit precision is less than 62, the closest double precision value to that 80 bit value is in fact 62.
Similarly, if you compile your original program with the 64 bit compiler, then arithmetic is performed in the SSE unit which has no 80 bit registers. And so there is no 80 bit intermediate value and your program outputs 62.
Or, going back to the 32 bit compiler, you can arrange that intermediate values are stored to 64 bit precision on the FPU and also achieve an output of 62. Call Set8087CW($1232) to achieve that.
As you can see, binary floating point arithmetic can sometimes be surprising.
If you use Round rather than Trunc then the value returned will be the closest integer, rather than rounding towards zero as Trunc does.
But perhaps a better solution would be to use a decimal data type rather than a binary data type. If you do that then you can represent 0.62 exactly and thereby avoid all such problems. Delphi's built in decimal real valued data type is Currency.
Use round instead of trunc.
round will round towards the nearest integer, and 62.00 is very close to 62, so there is no problem. trunc will round to the nearest integer towards zero, and 62.00 is very close to 61.9999999 so numeric 'fuzz' might very well cause the issue you describe.

COBOL Packed data type: Type = P5

This may be a very basic question for COBOL experts. But I till date had nothing to do with COBOL. We are processing some files based on character position. The files are being sent to us from mainframe machines and we have a layout file for that that says somethings like this.
POSITION : LENGTH : TYPE : DESCRIPTION
----------:--------:------:-------------------------------
61-70 : 10 : P5 : FIELD-1 9(13)V(05)
71-80 : 10 : P5 : Field-2 9(13)V(05)
81-81 : 1 : A/N : FLAG
82-84 : 3 : N : NUMBER OF DAYS 9(3)
I understand that the type A/N means it is alpha-numeric. N means numeric and P means Packed data type. What i dont understand is what P5 means. What is the significance of 5 that comes next to P?
What is the significance of 5 that comes next to P?
I'm not sure. Five 16 bit words, maybe.
Your packed fields are 10 bytes and holding 19 characters (18 digits plus the sign). The decimal point is implied.
If the sign byte (the rightmost byte) is anything other than hexadecimal F, update your question.
If you could update your question with five hexadecimal strings representing five of the numbers, that would be great.
Right now, I'm guessing that it's an ordinary packed decimal field.
P - packed decimal (i.e. Cobol Comp-3) a 18 digit packed decimal would occupy 10 bytes which agrees with the lengths give
5 - number of digits after the decimal point (at a guess).
Field definition in cobol is probably
03 FIELD-1 pic s9(13)V(05) comp-3.
in packed decimal, the sign is held in the last nyble (4 bits) and each nyble (4 bits) holds one decimal digit.
i.e.
121 is represented as x'121c'
while
-121 is represented as x'121d'
If you are using java and can get the cobol copybook, there are packages that can read the file using the cobol copybook.
I would bet it means 5 decimal places.

Resources