【问题标题】:Is it safe to cast Math.Round result to float?将 Math.Round 结果转换为浮点数是否安全?
【发布时间】:2017-12-17 02:59:33
【问题描述】:

一位同事写了一些代码:

var roundedNumber = (float) Math.Round(someFloat, 2);
Console.WriteLine(roundedNumber);

我不确定此代码 - 此处写入的数字是否还能保证有 2 位小数?在我看来,将双精度 Math.Round(someFloat, 2) 截断为 float 可能会导致字符串表示超过 2 位的数字。任何人都可以提供一个例子(证明这样的演员表是不安全的)或者以某种方式证明执行这样的演员表是安全的吗?

【问题讨论】:

  • 为了显示目的,Console.WriteLine(${someFloat:F2}) 更简单,更清楚地传达了它的意图。
  • 这个answer 并不完全符合您的问题,但可能会对您有所帮助。
  • 这似乎不是显示问题。 OP 似乎想要一个四舍五入的float,而不是一个四舍五入的double
  • @itsme86 当然 - 我在上面的代码示例中简化/说谎。在我的真实代码中,浮点数是从服务类中的方法返回的,然后在一些数据结构中传递,然后在一些 JSON 中编码并传递给前端应用程序,等等。但我感兴趣的是一个狭窄的问题,即强制转换为浮点数是否会导致.ToString() 表示不再有 2 个小数位的数字,而这个简单的例子——我承认它显然是错误的代码——足以说明这个问题。
  • @MarkAmery 抱歉,但您能以某种方式重新表述您的问题吗? rounded 值的字符串表示在分隔符后有两个以上的十进制数字:var single= Single.MaxValue; Console.WriteLine(single); var rounded = (Single)Math.Round(single, 2); Console.WriteLine(rounded); rounded 因为字符串在我的机器上看起来像 3,402823E+38

标签: c# floating-point rounding


【解决方案1】:

假设单精度和双精度 IEEE754 表示和规则,我已经检查了前 2^24 个整数 i

float(double( i/100 )) = float(i/100)

换句话说,将一个小数点后 2 位的十进制值转换两次(首先转换为最接近的双精度,然后转换为最接近的单精度浮点数)与将小数直接转换为单精度是一样的,只要整数部分小数不太大。

我无法保证更大的值。

双重近似和单一近似是不同的,但这不是真正的问题。

至少在167772.16 之前进行两次转换是无害的,就像 Math.Round 直接以单精度完成它一样。

这是带有 ArbitraryPrecisionFloat 包的 Squeak/Pharo Smalltalk 中的测试代码(很抱歉没有在 c# 中展示它,但语言并不重要,只有 IEEE 规则才可以)。

(1 to: 1<<24)
    detect: [:i |
        (i/100.0 asArbitraryPrecisionFloatNumBits: 24) ~= (i/100 asArbitraryPrecisionFloatNumBits: 24) ] 
    ifNone: [nil].

编辑

以上测试是多余的,因为感谢 Mark Dickinson (Innocuous double rounding of basic arithmetic operations) 提供的出色参考,我们知道执行 float(double(x) / double(y)) 会为 x / y 生成正确舍入的值,只要 x 和 @987654330 @ 都可以表示为浮点数,任何0 &lt;= x &lt;= 2^24y=100 都是这种情况。

编辑

我检查了分子高达 2^30(十进制值 > 1000 万),并且转换两次仍然与转换一次相同。在全球变暖的情况下,使用解释性语言走得更远……

【讨论】:

  • “语言并不重要,只有 IEEE 规则才重要” – 但我不相信 IEE 754 涵盖了双精度到单精度浮点数的转换,是吗? ?
  • @poke 至少在 IEEE754-2008 第 1.3 节中this standard specifies conversion between different floating point formats。如果我没记错的话,convertFormat 在第 5.4.2 节中介绍了它
  • @aka.nice:这是 IEEE 754 算术的定理,如果您有有限值 xy 可以精确表示为浮点数,那么执行 float(double(x) / double(y)) 会产生正确舍入的值为x / y+-*sqrt 也是如此。例如,请参阅this paper
  • @MarkDickinson 这种直觉推动了 2^24 的初始限制,但最好有证明,感谢您的参考!那么坏舍入的概率足够低,让我们走得更远......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-24
  • 2012-01-27
  • 1970-01-01
相关资源
最近更新 更多