浮点问题:C ++到C#迁移

时间:2014-01-22 04:30:19

标签: c# c++ c floating-point floating-point-precision

我正在开发一个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#中获得相同的结果。有可能吗?

5 个答案:

答案 0 :(得分:2)

事实:

  • 此函数在给定正常程序输入和
  • 的情况下返回C ++与C#的不同输出
  • 此函数返回C ++中相同的输出与C#给定的受控相同输入

表明:

  • 此函数的正常程序输入在C ++与C#中不同。

关于后者,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}}。但是,这只会在非常罕见的情况下导致不同的结果,其中shortd+.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)产生不同的结果。

当您从doubleintshort截断(丢失精度)时,这两个函数都有丢失数据的可能性。假设您的目标是MS环境sizeof(double) == 8sizeof(int) == 4sizeof(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) 如果比较浮点二进制点类型和浮点小数点类型,它将不会给出相同的结果。