( x + (1 << k) - 1 ) >> k
这样就可以保证负数通过这么一个偏置,其“右移”的结果也是向上舍入。
总结起来,就是 x / (2^k) 等价的右移表达式为:
( x < 0 ? x + (1<> k
IEEE浮点数舍入:
浮点数舍入相比向整数舍入,就复杂一些,IEEE定义了四种不同的舍入方式,默认的为向偶数舍入(round-to-even)。其他三个分别是向零舍入,向上舍入和向下舍入。
其他三个比较直观,就不再说明,以十进制为例说明“向偶数舍入”,平常的四舍五入中,以舍入到小数点后两位为例,如果第3位小数小于5,则舍去,大于等于5的则进1。向偶数舍入除了最中间的这个值(第3位小数为5)以外,其他与平常的舍入相同,中间的这个值,则要保证在舍入的时候舍入位必须为偶数。比如1.40, 1.60, 1.50, 2.50, -1.50向偶数舍入的结果分别为1, 2, 2, 2, -2
至于为什么要想偶数舍入,这是CSAPP(2e)中的原文:
The problem with such a convention is that one can easily imagine scenarios
in which rounding a set of data values would then introduce a statistical bias
into the computation of an average of the values. The average of a set of numbers
that we rounded by this means would be slightly higher than the average of
the numbers themselves. Conversely, if we always rounded numbers halfway
between downward, the average of a set of rounded numbers would be
slightly lower than the average of the numbers themselves. Rounding toward
even numbers avoids this statistical bias in most real-life situations.
It will round upward about 50% of the time and round downward about 50% of the time.
也就是如果在一组数据中,向偶数舍入一般既有向上舍入也有向下舍入,可以一定程度上消除统计平均数时由于总是向上舍入时所带来的统计偏差。
扩展成二进制,就是对形如 XX···X.YY···Y100···二进制模式位的数(最右边的Y是要舍入的位置),根据舍入位是否为偶数来选择是向上舍入还是向下舍入。(至于为什么是100,我还没想明白)
比如10.010, 10.011, 10.110, 11.001向偶数舍入(保留小数点后一位)的结果分别为10.0, 10.1, 11.0, 11.0。
总结:
向整数舍入总是向零舍入,因此为了保证负数右移 k 位的结果与除以 2^k 的结果是相同的,要做一个偏置。而浮点数舍入则为向偶数舍入,中间值根据舍入位是否为偶数来决定是向上还是向下舍入。
补充:对于V = 0.yyyyy··· 的无穷二进制位(其中 y 是一个 k 位的序列,例如 1/3 = 0.01010101···,y = 01,k = 2),满足以下公式:
V = Y / (2^k - 1)
推倒:V 左移 k 为为 y.yyyy···,也即 Y + V = V * 2^k,变换一下就可以得到公式。
用这种方法可以快速计算出某些二进制位代表的十进制数,比如 0.001001001··· = 1 / (2^3 - 1) = 1 / 7,0.000111000111000111··· = 7 / (2^6 - 1) = 7 / 63 = 1 / 9