Маршал C ++ с плавающей точкой к C # проблема точности с плавающей точкой - PullRequest
1 голос
/ 23 февраля 2011

У меня есть база данных DBase IV. В каждой строке есть памятное поле со строкой в ​​кодировке ASCII, которая содержит две сериализованные структуры borland c ++. Я могу извлечь данные с помощью OleDb, перекодировать их в ascii с помощью класса ASCIIEncoding, преобразовать их в байты с помощью BinaryReader и преобразовать их в мою структуру C # с помощью Marshal.PtrToStructure. Данные, которые я получаю, верны, но любое значение с плавающей запятой, которое слишком велико в базе данных, совершенно неверно, когда оно приводится к c #. Например, значение 1149.00 приведено к 764.9844, но значение как 64.00 приведено к штрафу. Я могу опубликовать часть кода и структур, но я решил, что сначала я старался сделать его коротким. Я знаю, что значения с плавающей точкой имеют точность до 7 цифр, но я не понимаю, почему я вижу это, потому что значения ниже этого предела.

Edit:

struct cplusplusstruct // from the c++ code
{
  int   Number;
  float P;
  float ZP;
  float Hours;
  int   Month;
  int   Day;
  int   Year;
  int   Hour;
  int   Minute;
  int   Second;
  ULONG UPCTime;
  int   B;
  char  Name[21];
  float L;
  float H;
  float S;
}


[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct csharpstruct  //The C# struct I created
{
     public int Number;
     public float Pr;
     public float ZP;
     public float Hours;
     public int Month;
     public int Day;
     public int Year;
     public int Hour;
     public int Minute;
     public int Second;
     public UInt32 UPCTime;
     public int B;
 [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 21)]
     public string Name;
     public float L;
     public float H;
     public float S;
 }


//OLE DB Connection and query ...

//Casting data to struct
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] blob = encoding.GetBytes(memoString);
MemoryStream memoryStream = new MemoryStream(blob);
BinaryReader binaryReader = new BinaryReader(memoryStream);

int dataSize = Marshal.SizeOf(typeof(csharpstruct));
GCHandle handle = GCHandle.Alloc(binaryReader.ReadBytes(dataSize), GCHandleType.Pinned);
csharpstruct data = (csharpstruct) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(csharpstruct));

Редактировать: Ниже приведен код Java, который отлично читает данные, но без использования приведения.

org.xBaseJ.DBF dbf = new org.xBaseJ.DBF(dbPath);
org.xBaseJ.DBF dbf = new org.xBaseJ.DBF(dbPath);
MemoField m = (MemoField) dbf.getField("MEMOFIELD");

Charset charset = Charset.forName("US-ASCII");
CharsetDecoder decoder = charset.newDecoder();
ByteBuffer trendBytes = ByteBuffer.wrap(m.getBytes());
trendBytes.order(ByteOrder.LITTLE_ENDIAN);
trendBytes.getInt();
trendBytes.getFloat();

Ответы [ 2 ]

3 голосов
/ 24 февраля 2011

У вас есть Pack = 1 в вашей структуре C #, но вы не сказали, упакована ли ваша структура C ++.Так как у вас есть поле нечетного размера непосредственно перед вашими числами (строка из 21 символа), это может вызвать проблемы и означать, что ваши значения с плавающей точкой читаются неправильно.Все, что раньше, имеет длину 4 байта, поэтому упаковка вряд ли вызовет у вас проблемы.Я хотел бы убедиться, что упаковка совпадает как в C #, так и в C ++, прежде чем идти дальше.

0 голосов
/ 08 марта 2011

Я не смог решить проблему напрямую.Проблема, кажется, исходила от поставщика данных OLE, который я использовал.Данные, полученные из базы данных, немного отличались от данных, предоставленных xBaseJ .В итоге я преобразовал xBaseJ в байт-код CLI, используя IKVM.NET .Это позволило мне заменить поставщика данных OLE на читатель xBaseJ.Остальная часть моего кода осталась без изменений.

...