猿问

格式化双打以在C#中输出

运行与.NET中的双重乘法是否中断相关的快速实验?并阅读了几篇有关C#字符串格式的文章,我认为是这样的:


{

    double i = 10 * 0.69;

    Console.WriteLine(i);

    Console.WriteLine(String.Format("  {0:F20}", i));

    Console.WriteLine(String.Format("+ {0:F20}", 6.9 - i));

    Console.WriteLine(String.Format("= {0:F20}", 6.9));

}

将与此C代码的C#等效:


{

    double i = 10 * 0.69;


    printf ( "%f\n", i );

    printf ( "  %.20f\n", i );

    printf ( "+ %.20f\n", 6.9 - i );

    printf ( "= %.20f\n", 6.9 );

}

但是,C#会产生输出:


6.9

  6.90000000000000000000

+ 0.00000000000000088818

= 6.90000000000000000000

尽管我在调试器中显示的值等于6.89999999999999946709(而不是6.9)。


与C相比,后者显示了格式要求的精度:


6.900000                          

  6.89999999999999946709          

+ 0.00000000000000088818          

= 6.90000000000000035527          

这是怎么回事?


(Microsoft .NET Framework版本3.51 SP1 / Visual Studio C#2008 Express Edition)


我有数值计算方面的经验,并且在各种平台上实现间隔算术(一种用于估计由于复杂数值系统中的精度限制而导致的误差的技术)的经验。为了获得奖励,请不要尝试说明存储精度-在这种情况下,这是64位双精度ULP的区别。


为了获得赏金,我想知道.Net如何(或是否)可以将C格式的double格式化为所需的精度。


眼眸繁星
浏览 393回答 3
3回答

HUWWW

问题是,.NET始终会在应用格式之前四舍五入double为15位有效的十进制数字,而不管您的格式要求的精度和二进制数的确切十进制值如何。我猜想Visual Studio调试器有其自己的格式/显示例程,这些例程直接访问内部二进制数,因此C#代码,C代码和调试器之间存在差异。没有内置的函数可以访问a的确切十进制值double,也无法double将a 格式化为特定的小数位数,但是您可以自己完成此工作,方法是将内部二进制数拆开并重建为十进制值的字符串表示形式。另外,您可以使用Jon Skeet的DoubleConverter类(从他的“二进制浮点和.NET”文章链接到)。该ToExactString方法具有返回a的精确十进制值的方法double。您可以轻松地对此进行修改,以将输出舍入为特定精度。double i = 10 * 0.69;Console.WriteLine(DoubleConverter.ToExactString(i));Console.WriteLine(DoubleConverter.ToExactString(6.9 - i));Console.WriteLine(DoubleConverter.ToExactString(6.9));// 6.89999999999999946709294817992486059665679931640625// 0.00000000000000088817841970012523233890533447265625// 6.9000000000000003552713678800500929355621337890625

湖上湖

Digits after decimal point// just two decimal placesString.Format("{0:0.00}", 123.4567);      // "123.46"String.Format("{0:0.00}", 123.4);         // "123.40"String.Format("{0:0.00}", 123.0);         // "123.00"// max. two decimal placesString.Format("{0:0.##}", 123.4567);      // "123.46"String.Format("{0:0.##}", 123.4);         // "123.4"String.Format("{0:0.##}", 123.0);         // "123"// at least two digits before decimal pointString.Format("{0:00.0}", 123.4567);      // "123.5"String.Format("{0:00.0}", 23.4567);       // "23.5"String.Format("{0:00.0}", 3.4567);        // "03.5"String.Format("{0:00.0}", -3.4567);       // "-03.5"Thousands separatorString.Format("{0:0,0.0}", 12345.67);     // "12,345.7"String.Format("{0:0,0}", 12345.67);       // "12,346"ZeroFollowing code shows how can be formatted a zero (of double type).String.Format("{0:0.0}", 0.0);            // "0.0"String.Format("{0:0.#}", 0.0);            // "0"String.Format("{0:#.0}", 0.0);            // ".0"String.Format("{0:#.#}", 0.0);            // ""Align numbers with spacesString.Format("{0,10:0.0}", 123.4567);    // "     123.5"String.Format("{0,-10:0.0}", 123.4567);   // "123.5     "String.Format("{0,10:0.0}", -123.4567);   // "    -123.5"String.Format("{0,-10:0.0}", -123.4567);  // "-123.5    "Custom formatting for negative numbers and zeroString.Format("{0:0.00;minus 0.00;zero}", 123.4567);   // "123.46"String.Format("{0:0.00;minus 0.00;zero}", -123.4567);  // "minus 123.46"String.Format("{0:0.00;minus 0.00;zero}", 0.0);        // "zero"Some funny examplesString.Format("{0:my number is 0.0}", 12.3);   // "my number is 12.3"String.Format("{0:0aaa.bbb0}", 12.3);

红颜莎娜

尽管这个问题已经结束,但我认为值得一提的是这种暴行是如何产生的。在某种程度上,您可能会指责C#规范,该规范指出双精度数必须具有15或16位的精度(IEEE-754的结果)。进一步(第4.1.6节)指出,允许实现使用更高的精度。注意:更高,而不是更低。他们甚至允许从IEEE-754偏离:类型的表达式x * y / z,其中x * y将产生+/-INF,但将是一个有效的范围分割后,不必导致错误。此功能使编译器更容易在可以产生更好性能的体系结构中使用更高的精度。但是我答应了“理由”。这里有一个引号(您请求在你最近的评论一个资源)从共享源代码CLI中clr/src/vm/comnumber.cpp:“为了提供既便于显示又可往返的数字,我们使用15位数字解析该数字,然后确定它是否往返为相同的值。如果是,则将NUMBER转换为字符串,否则用17位数字重新解析并显示出来。”换句话说:MS的CLI开发团队决定既可以双向访问又可以显示漂亮的值,这些值并不难读。是好是坏?我希望选择加入或退出。找出任何给定数字的这种往返性的技巧是什么?转换为通用NUMBER结构(对于double的属性具有单独的字段),然后再返回,然后比较结果是否不同。如果不同,则使用精确值(与中的值6.9 - i相同),如果相同,则使用“漂亮值”。就像您在对Andyp的评论中提到的那样,6.90...00等于6.89...9467。现在您知道为什么0.0...8818使用它了:它与有所不同0.0。这15位数字是硬编码的,只能通过重新编译CLI,使用Mono或调用Microsoft并说服他们添加一个选项来打印完整的“精度”来更改(这不是真正的精度,但是由于缺少一个更好的词)。自己计算52位精度或使用前面提到的库可能会更容易。编辑:如果您想用IEE-754浮点进行实验,请考虑使用此在线工具,该工具向您显示浮点的所有相关部分。
随时随地看视频慕课网APP
我要回答