我正在开发一个C ++到C#的迁移项目。我遇到了一个涉及浮点运算的问题。 在C ++中,有一个函数
int doubleToInt(double d)
{
return (int)(d >= 0.0 ? (d + 0.1) : (d - 0.1));
}
我迁移到C#的功能相同(注意,在C ++中,sizeof(int)
为2 bytes
。所以我使用short
作为返回类型)
private static short doubleToInt(double d)
{
return (short)(d >= 0.0 ? (d + 0.1) : (d - 0.1));
}
转换完成后,我正在进行一些操作并生成二进制文件。与C ++相比,在C#中生成的二进制文件是不同的。即使我在调试时(在写入文件之前)比较值,我也会得到不同的答案。
现在我需要解释我的客户为何与众不同。
Can someone give me inputs on why it is different?
我所知道的是,在进行浮点运算时,C ++中生成的临时值具有更高的精度。
Are there any other points? So that I can defend by telling "The way C++ handles the floating point is different from C#
或者我可以修改C#程序以匹配C ++输出吗?可能吗?另外,我无法修改C ++遗留代码。我需要在C#中获得相同的结果。有可能吗?
答案 0 :(得分:2)
事实:
表明:
关于后者,OP在评论中指出“我还在C ++和C#中创建了一个示例测试应用程序并对输入进行了硬编码。通过硬编码输入到doubleToInt函数,我得到相同的结果。“这表明,给定相同的输入,函数的C ++和C#版本返回相同的输出。我们可以从中推断出不同输出的原因是不同的输入。
OP还指出“在调试时,为了比较结果,如果我看到C ++和C#的输出,则对于同一组值的输出是不同的。”但是,这是不确定的,因为调试器和打印语句用于调试通常不会打印浮点对象的完整,准确值。通常,它们会四舍五入到六位数。例如,一个简单的std::cout << x
将10000.875和10000.9375显示为“10000.9”,但它们是不同的数字,会在doubleToInt
中产生不同的输出。
总之,问题可能是在调用doubleToInt
之前,程序中的早期工作会遇到浮点舍入或其他错误,并在C ++和C#版本中将不同的值传递给doubleToInt
。要对此进行测试,请将完全输入打印到doubleToInt
,看看它们在两个版本中是否有所不同。
准确打印输入可以通过以下方式完成:
%a
格式。 (这是一个C函数,用于以十六进制浮点表示法打印浮点值。某些C ++库在使用printf
时支持它。)std::cout.precision(100)
一样。某些C ++实现可能仍然无法打印确切的值(这是质量问题),但是它们应该打印足够的数字以区分确切的值与相邻的double
值。unsigned char
的指针并打印单个char
个对象)。根据提供的代码,问题不太可能是doubleToInt
中的浮点问题。语言定义允许浮点评估有些松懈,因此从理论上讲,d+.1
可能会以过高的精度而不是正常的double
精度进行评估,然后转换为int
或{ {1}}。但是,这只会在非常罕见的情况下导致不同的结果,其中short
以d+.1
精度计算得到一个整数,但过量精度计算的double
仍然低于整数。这要求大约38位(d+.1
有效位中的53位减去整数部分中的16位加上一位用于舍入)具有特定值,因此我们预计它偶然发生在2750亿次中的大约1位(假设均匀分布是合适的模型)。
事实上,添加.1告诉我,有人试图在他们期望为整数的结果中纠正浮点错误。如果有人有一个“自然”值,他们试图转换为整数,通常的方法是舍入到最接近的值(与double
一样),或者有时,截断。添加.1表明他们试图计算他们期望为整数的东西但由于浮点错误而得到3.999或4.001的结果,所以他们通过添加.1和截断来“纠正”它。因此,我怀疑程序中较早存在浮点错误。也许它们在C#中更加恶化。
答案 1 :(得分:0)
您正在尝试使用默认舍入来舍入数字。 C ++没有强制要求舍入方向,并且考虑到不同的结果,它可能与C#不同。
答案 2 :(得分:0)
如果给定平台上的technically
值超过double
,您的函数会sizeof(short/int)
产生不同的结果。
当您从double
到int
或short
截断(丢失精度)时,这两个函数都有丢失数据的可能性。假设您的目标是MS环境sizeof(double) == 8
,sizeof(int) == 4
和sizeof(short) == 2
;对于Windows环境中的C ++和C#都是如此(在MS版本中,endian-ness和bit-ness(32/64)与这些大小无关)。
在调用函数生成二进制输出之后,您还需要提供有关正在发生的事情的更多信息。从技术上讲,'二进制'文件输出只是无符号字符输出(即sizeof() == 1
);意味着你如何“写”函数输出到文件也会严重影响C ++和C#中的文件输出数字类型(double/int/short
)。
您是否在C ++中使用fopen
调用特定格式的文件输出,或者您使用的是std::fstream
(或其他内容)?你是如何用C#将数据写入文件的?您是否正在执行file.Write(doubleToInt(d))
之类的操作(假设您使用的是System.IO.StreamWriter
),或者您使用的是System.IO.FileStream
并将doubleToInt
输出转换为byte[]
然后打电话给file.Write(dtiByteArr)
?
根据给出的信息,我所说的最好的猜测是,当传入函数的值更大时,您的C#函数返回short
而不是int
导致问题比short.MaxValue
。
答案 3 :(得分:0)
我认为您的问题与数据(short
)如何写入/读取二进制文件有关。您需要考虑Big-Endian / Small-Endian,因此无论代码在什么平台上,数据文件都是一致的。
检查System.BitConverter课程。 BitConverter.IsLittleEndian字段可以帮助您进行转换。代码应类似于以下内容:
short value = 12348;
byte[] bytes = BitConverter.GetBytes(value);
Console.WriteLine(BitConverter.ToString(bytes));
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
Console.WriteLine(BitConverter.ToString(bytes)); // write to your file
答案 4 :(得分:-1)
我还没有完全了解它,所以也许我错了,但它可能与这里提到的内容有关: In a thread about the difference on Float, Decimal and Double
正如嘿所说:你正在使用的Double,在C#中是一个浮点二进制点类型。 (10001.10010110011) 也许,C ++中的Double更像是C#中的十进制小数浮点型。 (12345.65789) 如果比较浮点二进制点类型和浮点小数点类型,它将不会给出相同的结果。