C++元帅float 转 C# float 精度问题
我有一个 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您的 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.我无法直接解决问题。问题似乎来自我正在使用的 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.