TDbf has issues when writing large float values
Original Reporter info from Mantis: wp @wpam
-
Reporter name:
Original Reporter info from Mantis: wp @wpam
- Reporter name:
Description:
When writing large numbers to a float field of a dbf table two kinds of errors have been observed by me:
- The order of magnitude is not written correctly, e.g. when writing the value 1E20 the value 1E19 may appear in the db.
- When writing very large values an access violation can occur
Steps to reproduce:
Run the attached demo
- It attempts to write the value 1E20 to a float field set up with Size=20 and Precision=5. Reading the field after posting, however, yields the value 1E19 instead of 1E20.
- Replace the value to be written by 1E23. Now an access violation happens.
Additional information:
The issue occurs in the procedure FloatToDbfStr of unit dbf_dbffile:
procedure FloatToDbfStr(const Val: Extended; const Size, Precision: Integer; const Dest: PChar);
var
Buffer: array [0..24] of Char;
resLen: Integer;
iPos: PChar;
begin
// convert to temporary buffer
resLen := FloatToText(@Buffer[0], Val, {$ifndef FPC_VERSION}fvExtended,{$endif} ffFixed, Size, Precision);
// prevent overflow in destination buffer
if resLen > Size then
resLen := Size;
...
It uses the function FloatToText to convert the value to be written to a string. It does it in fixed format - as aconsequence the string can become very long for very large numbers. FloatToText, however, requires a preallocated buffer for the result string, and this buffer is overflowing when the string is too long.
My solution: Use FloatToStrF which simply returns a string and thus automatically takes care of buffer allocation.
In order to avoid another overflow in the record buffer into which the result string will be inserted at the end of the FloatToDbfStr procedure the procedure checks whether the string length (resLen) is greater than the space reserved in the buffer (Size). When the string is too long it is simply chopped to the correct size. This is ok when the lost digts are decimals, but it is absolutely wrong when the list digits are "in front of the decimal point" as is can happen with large numbers.
My solution here: Rather than truncating the result string apply FloatToStrF a second time, but now with exponential format so that numbers of any size fit into the string length allowed (of course within the limits of the data type).
These modifications are used in the attached patch. After application of the patch, the issues reported are gone.
Mantis conversion info:
- Mantis ID: 38999