C++元帅float 转 C# float 精度问题

发布于 2024-10-19 04:27:05 字数 2171 浏览 2 评论 0原文

我有一个 DBase IV 数据库。每行都有一个备注字段,其中包含一个 ASCII 编码字符串,其中包含两个序列化的 borland c++ 结构。我可以使用 OleDb 提取数据,使用 ASCIIEncoding 类将其重新编码为 ascii,使用 BinaryReader 将其转换为字节,并使用 Marshal.PtrToStructure 将其转换为我的 C# 结构。我得到的数据是正确的,但数据库中任何太大的浮点数在转换为 c# 时都是完全错误的。例如,值 1149.00 转换为 764.9844,但像 64.00 这样的值转换得很好。我可以发布一些代码和结构,但我想我一开始就尽量保持简短。我知道浮点数最多只能精确到 7 位数字,但我很困惑为什么会看到这个,因为这些值低于该限制。

编辑:

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();

I have a DBase IV database. Each row has a memo field with a ASCII encoded string that holds two serialized borland c++ structures. I am able to pull the data using OleDb, re-encode it to ascii using the ASCIIEncoding class, convert it to bytes using a BinaryReader, and cast it to my C# struct using Marshal.PtrToStructure. The data I get is correct but any float that is to big in the database is completely wrong when it is cast to the c#. For example, a value of 1149.00 cast into 764.9844 but a value like 64.00 cast fine. I can post some of the code and the structures but I figured I tried to keep it short at first. I know that floats are only precise up to 7 digits but I'm confused why I'm seeing this because the values are under that limit.

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));

Edit: The following is java code that read the data just fine but without any use of casting.

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();

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

孤独患者 2024-10-26 04:27:05

您的 C# 结构中有 Pack = 1,但没有说明您的 C++ 结构是否已打包。由于您的浮点数(21 个字符的字符串)之前有一个奇数大小的字段,这可能会导致问题并意味着您的浮点数读取未对齐。之前的所有内容都是 4 个字节长,因此打包不太可能给您带来问题。在继续之前,我会确保 C# 和 C++ 中的打包都匹配。

You have Pack = 1 in your C# struct, but have not said whether your C++ struct is packed. Since you have an odd-sized field just before your floats (the 21-character string) that might cause trouble and mean that your floats are being read mis-aligned. Everything before there is 4 bytes long so packing is less likely to cause you problems. I would make sure that the packing matches in both C# and C++ before going any further.

八巷 2024-10-26 04:27:05

我无法直接解决问题。问题似乎来自我正在使用的 OLE 数据提供程序。从数据库检索的数据与 xBaseJ 提供的数据略有不同。我最终使用 IKVM.NET 将 xBaseJ 转换为 CLI 字节码。这使我能够用 xBaseJ 读取器替换 OLE 数据提供程序。我的其余代码保持不变。

I wasn't able to solve the issue directly. The problem seemed to come from the OLE data provider I was using. The data retreived from the database was slightly different then what xBaseJ provided. I ended up converting xBaseJ to CLI bytecode using IKVM.NET. This allowed me to replace the OLE data provider with the xBaseJ reader. The rest of my code remained unchanged.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文