StrToFloat and who is wrong: Delphi or ASE/SQL Server - delphi

Recently I found strange thing: result of
var
d: double;
begin
d := StrToFloat('-1.79E308');
is not the same as string value '-1.79E308' converted to float field type by ASE and SQL Server through
INSERT INTO my_table (my_float_field) VALUES (-1.79E308)
For Delphi memory dump is 9A BB AD 58 F1 DC EF FF
For ASE/SQL Server value in packet on select is 99 BB AD 58 F1 DC EF FF.
Who is wrong, both servers or Delphi?

The premise that we are working from is that StrToFloat yields the closest representable binary floating point value to the supplied decimal value.
The two hexadecimal values the you present are adjacent. You can see that they differ by 1 in the significand. Here is some Python code that decodes the two values:
>>> import struct
>>> struct.unpack('!d', 'ffefdcf158adbb9a'.decode('hex'))[0]
-1.7900000000000002e+308
>>> struct.unpack('!d', 'ffefdcf158adbb99'.decode('hex'))[0]
-1.79e+308
Bear in mind that Python prints floating point values using the shortest possible significant for which the closest representable value is the actual value. That ffefdcf158adbb99 decodes to a value the prints as -1.79e+308 in the eyes of Python, is sufficient proof that ffefdcf158adbb99 is the closest representable value. In other words, the Delphi code is giving the wrong answer.
And, just out of curiosity, in the opposite direction:
>>> hex(struct.unpack('<Q', struct.pack('<d', float('-1.79e308')))[0])
'0xffefdcf158adbb99L'
It is interesting to note that the 32 bit Delphi compiler yields ffefdcf158adbb99 but the 64 bit Delphi compiler yields ffefdcf158adbb9a. This is a clear defect, and should be submitted as a bug report to Quality Portal.

Related

Huffman Code with equal symbol frequencies

Starting with these frequencies:
A:7 F:6 H:1 M:2 N:4 U:5
at a later step I have 5 6 7 7, where one of the 7's is the "A". Which 7 branch I pick to be a 0 or a 1 is arbitrary.
So how do I get uniquely decodable code word?
You need to send the code to the receiver, not the frequencies. You can arbitrarily assign 0's and 1's to all of the branches, and then send the codes for each symbol before the coded symbols themselves. There are many possible Huffman codes from the same set of frequencies.
More commonly only the code lengths in bits for each symbol are sent. In this case those are A:2 F:2 H:4 M:4 N:3 U:2. Then a canonical code is used on both ends that depends only on the lengths. In this case, starting with 0's, the canonical code would be:
A: 00
F: 01
U: 10
N: 110
H: 1110
M: 1111
where codes of equal length are assigned to the symbols in lexicographical order. Note that the Huffman tree that was built is not needed. All that is needed is the number of bits for each symbol.

How to convert a floating point number to a string with max. 2 decimal digits in 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;

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.

Why delphi's RoundTo method behaves differently? [duplicate]

I expected that the result would be 87.29. I also tried SimpleRoundTo, but produces the same result.
In the help there is also a "strange" example:
ms-help://embarcadero.rs2010/vcl/Math.RoundTo.html
RoundTo(1.235, -2) => 1.24
RoundTo(1.245, -2) => 1.24 //???
Does anybody know which function I need to get the result of 87.29? I mean: If the last digit >= 5 round up, if < 5 round down. As taught in the school :)
I use Delphi2010, and SetRoundMode(rmNearest). I also tried with rmTruncate.
The value 87.285 is stored in a double variable.
Also strange:
SimpleRoundTo(87.285, -2) => 87.29
but
x := 87.285; //double
SimpleRoundTo(x, -2) => 87.28
The exact value 87.285 is not representable as a floating-point value in Delphi. A page on my Web site shows what that value really is, as Extended, Double, and Single:
87.285 = + 87.28500 00000 00000 00333 06690 73875 46962 12708 95004 27246 09375
87.285 = + 87.28499 99999 99996 58939 48683 51519 10781 86035 15625
87.285 = + 87.28500 36621 09375
By default, floating-point literals in Delphi have type Extended, and as you can see, the Extended version of your number is slightly higher than 87.285, so it is correct that rounding to nearest would round up. But as a Double, the real number is slightly lower. That's why you get the number you expected if you explicitly store the number in a Double variable before calling RoundTo. There are overloads of that function for each of Delphi's floating-point types.
87.285 is not exactly representable and the nearest double is slightly smaller.
The classic reference on floating point is What Every Computer Scientist Should Know About Floating-
Point Arithmetic.
For currency based calculations, if indeed this is, you should use a base 10 number type rather than base 2 floating point. In Delphi that means Currency.

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