I'm struggling to figure out the best method to convert from a real type to a big integer type.
The only way I can think of easily & reliably doing it, is to:
1- use the Int function to truncate the factional/decimal part.
2- use the FloatToDecimal procedure to return a TFloatRec record.
3- use TFloatRec.Digits to get a string containing the most significant digits
4- use TFloatRec.Exponent to get a count of total number of digits
5- adjust the digits from step 3, by adding zeros to the (least significant) end, so that the length equals the number of digits from step 4.
I don't like the conversion to an intermediate string type, it seems to me there must be a cleaner way.
Then, there are the problems of dealing with rounding errors in the least significant end of the real number. For example, here's a small test program I wrote
program test_real_3;
uses sysutils,math;
var
R :real;
I :longint;
R_FLOATREC :TFloatRec;
begin
R:= (1); for i:= 1 to 308 do R:= R*10;
R:= Int(R);
writeln('Int(R) = ',R);
FloatToDecimal(R_FLOATREC, R, 99, 0);
writeln('R_FLOATREC.digits = ',R_FLOATREC.digits,' R_FLOATREC.Exponent = ',R_FLOATREC.Exponent);
end.
The program creates a real number with the largest allowable number of digits.
The output is
Int(R) = 9.9999999999999981E+307
R_FLOATREC.digits = 99999999999999981 R_FLOATREC.Exponent = 308
The problem here is that because of rounding errors at the least significant end, the real variable does not match the original value. Is there a way round this? Do I just have to accept that the converted value might look significantly different to the original value? Is there some clever mathematical trick that might be able to recover the original (correct) value? Or maybe get closer to it than 99999999999999981000... ? Because that's a difference of 19x(10^291)... a very significant number. I've tried the Round function, but it can't handle real numbers with extremely large exponents.
Here's another small test program I wrote
program test_real_4;
uses sysutils,math;
var
R,R_MAN :real;
I,R_EXP :longint;
begin
R:= (1); for i:= 1 to 308 do R:= R*10;
R:= Int(R);
writeln('Int(R) = ',R);
frexp(R,R_MAN,R_EXP);
writeln('Frexp man = ',R_MAN,' exp = ',R_EXP);
end.
The output is
Int(R) = 9.9999999999999981E+307
Frexp man = 5.5626846462680024E-001 exp = 1024
This is calling Frexp, but the mantissa and exponent do not make sense to me, when I compare them with the original value, or compare them with the values returned in the previous test program.
Is the mantissa in decimal, but the expononent binary? Does this mean that (in this particular example) the mantissa must be multiplied by (2 ^ 1024), or shifted left (up) by 1024 bits?
I realise that I've probably stumbled into a hornets nest of a problem, and that these problems are unavoidable side-effects of real number types. So I'm not expecting anyone to write my code for me! Just some advice will do.
Thanks.