将Math.Round结果转换为float是否安全?

时间:2017-07-12 15:22:20

标签: c# floating-point rounding

一位同事写了一些代码:

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

我对这段代码有不确定性 - 这里写的数字是否保证再有2位小数?对我来说似乎有理由将双Math.Round(someFloat, 2)截断为float可能会导致其字符串表示形式超过2位的数字。任何人都可以提供一个这样的例子(证明这样的演员表是不安全的)或者以某种方式证明 安全地执行这样的演员表吗?

1 个答案:

答案 0 :(得分:0)

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

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

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

我无法保证更大的价值。

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

转换两次是无害的至少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生成正确舍入的值,只要xy都可以表示为浮点数,适用于任何0 <= x <= 2^24y=100

修改

我已经检查过最多2 ^ 30(十进制值> 10百万)的分子,并且转换两次仍然与转换一次相同。全球变暖使得解释性语言更进一步......