I have a MySQL database with a table containing many columns declared as float
type, e.g.TgPhi_L1_Min FLOAT DEFAULT NULL,
. The table is filled by a program written in C++Builder 10.3 using FireDAC (TFDQuery
) that collects data from a measuring device. The code responsible for that looks like this:
FDQuery_InsertData->Open("SELECT * FROM {id measurements_general} WHERE 1=2");
FDQuery_InsertData->Insert();
FDQuery_InsertData->FieldByName("meter_id")->AsInteger = MeterId;
FDQuery_InsertData->FieldByName("cfg_id")->AsInteger = ConfigId;
FDQuery_InsertData->FieldByName("date_time")->AsDateTime = DateTime;
FDQuery_InsertData->FieldByName("insert_date_time")->AsDateTime = Now();
for (auto i = 0; i < DescriptorsGeneral.List->Count; i++)
{
if (IsNan(Data[DescriptorsGeneral.Indexes[i]]) || IsInfinite(Data[DescriptorsGeneral.Indexes[i]]))
continue;
FDQuery_InsertData->FieldByName(DescriptorsGeneral.List->Strings[i])->AsSingle = Data[DescriptorsGeneral.Indexes[i]]; //Data is passed as a parameter to this function, float *Data;
}
FDQuery_InsertData->Post();
Everything goes fine, until someday a measuring device sent a value which is the maximum float
value of 0x7F7FFFFF
. It shouldn't be a problem, but I got an exception
First chance exception at $772A3642. Exception class EMySQLNativeException with message '[FireDAC][Phys][MySQL] Out of range value for column 'TgPhi_L1_Min' at row 1'. Process Meter_Server.exe (12796).
Trying to understand what happened, I turned on FireDAC's monitor, and it turns out that the value inserted into the INSERT
statement indeed exceeds the float
range i.e. 3.4028235E38
. What I find more surprising is that numbers passed to the INSERT
statement differ in precision, although all are declared the same way (MySQL float
, FireDAC single
), eg 0.98364758
, 0
, 3.4028235E38
, -3.4028235E38
, 0
, 3.4028235E38
, -3.4028235E38
, 29.885546
.
The parameters of fetching columns by TFDQuery
are: Col add [Index=86, SrcName="TgPhi_L1", SrcType=Single, SrcSize=0, SrcPrec=12, SrcScale=31, Type=Single, Size=0, Prec=12, Scale=31]
.
I tried with FormatOptions
: CheckPrecision
and Round2Scale
, I added my own mapping rules setting ScaleMin
and PrecMin
, but none of it affected values in the INSERT
statement sent to the database by FireDAC.
Can anyone help me solve this problem? what I am doing wrong?
EDITED:
I'm confused, of course when I prepare a proper string manually, as @GarrGodfrey suggested, it works, but it doesn't work with TFDQuery
in the way I did it. TFDQuery
knows that the field is the single
type, and finally always converts and rounds the values to single precision. The problem is, the single
type fields have a Precision
property set to 7, which complies with the float
standard. But in MySQL I'm not sure, this is where I'm lost.
In C++, the following two numbers are treated as equal:
float x = 3.4028235E38;
float y = 3.402823466385288E38;
I think, this is because y
has much more precision and is rounded. In MySQL, these are two different numbers: x
is out of range and y
is not, why?
CREATE PROCEDURE `test_float`()
begin
declare x float;
declare y float;
set x = 3.4028235E38;
set y = 3.402823466385288E38;
select x, y;
end