如何在C++中正确归一化浮点值?

7
也许我对IEEE754标准不是很了解,但是假设给定一组浮点数值,它们是floatdouble类型的,例如:
56.543f 3238.124124f 121.3f ...

你可以将它们转换为0到1之间的值,因此您需要规范化它们,通过考虑集合中的最大值和最小值并取一个适当的公共因子来完成。
现在我的观点是,如果与第一组中需要的精度水平相比,我需要更高精度的目标集,该集范围从0到1,特别是如果第一组值涵盖广泛的数字值(非常大和非常小的值)。
在这种情况下,floatdouble(或IEEE 754标准,如果您愿意)类型如何处理此情况,同时为第二组值提供更多精度,而我基本上不需要整数部分?
还是它根本无法处理这个问题,我需要使用完全不同的定点数学类型?

2
请注意,在C++中,IEEE浮点数并非强制要求。 - PlasmaHH
@PlasmaHH:有没有不实现IEEE 754的编译器?或者更确切地说,哪些现代CPU架构没有实现它? - thokra
1
@thokra:通常不是编译器的问题,而是硬件的问题。我目前能想到的一个例子是VAX。 - PlasmaHH
1
你为什么想要将值映射到[0,1]之间?如果你是出于某种精度提高的考虑,那么最好不要这样做,因为它并不能给你带来任何好处。如果你有其他原因需要这样做,那么你应该解释清楚,以便我们理解它与浮点运算的交互方式。 - Eric Postpischil
@user2485710:在这种情况下,“感知到”并不是指数据查看者会感知到的错误,而是指提问者对于浮点数在区间[0,1]上相对于另一个区间[0,M]有更高精度的感知。 - Eric Postpischil
显示剩余2条评论
5个回答

6
浮点数的存储格式类似于科学计数法。在内部,它们将二进制表示的前导1与尾数对齐。每个值都具有相对于自身数量级相同数量的二进制数字精度。
当你将一组浮点值压缩到0..1范围时,唯一会产生精度损失的是过程中发生的四舍五入。
如果仅通过缩放来进行压缩,则只会在尾数最低位附近(大约1或2 ulp,其中ulp表示“最后一位单位”)失去少量精度。
如果你还需要移动数据,那么事情就变得棘手了。如果你的数据都是正数,那么减去最小的数不会损坏任何东西。但是,如果你的数据是正负混合的,则一些接近零的值可能会失去精度。
如果你在double精度下执行所有算术运算,则将通过计算携带53位精度。如果你的精度需求适合这个范围(很可能适合),那么就没有问题。否则,确切的数值性能将取决于你的数据分布。

“由于发生的四舍五入”,是的,但我在给定的浮点类型中还有未使用的位,因此我在浪费位的同时将规范化的值四舍五入,你能理解我的观点吗? - user2485710
在IEEE 754 64位二进制中,从0x0到0x3ff0_0000_0000_0000的每个位模式都代表[0,1]中的一个不同数字,因此您浪费的比特少于64位中的3位。我更担心的是四舍五入。 - Patricia Shanahan
@PatriciaShanahan:那是10个比特。 - Moberg

3

将具有隐式前导1的二进制浮点值表示为

(1+fraction) * 2^exponent where fraction < 1

a/b的商是:

a/b = (1+fraction(a)) / (1+fraction(b)) * 2^(exponent(a) - exponent(b))

因此,除法/乘法基本上没有精度损失。
减法a-b的计算方式如下:
a-b = (1+fraction(a)) * 2^(exponent(a) - (1+fraction(b)) * exponent(b))

因此,减法/加法可能会造成精度损失 (big - tiny == big)!
将值 x 限制在范围 [min, max] 中,并将其限制在 [0, 1]。
(x - min) / (max - min)

如果任何减法操作存在精度损失,则可能会出现精度问题。

回答您的问题:

没有一种通用解决方案,需要为您的算法和期望数据选择合适的表示方法(浮点数、分数、多重精度等)。


3
单精度和双精度IEEE浮点数的格式中,指数和小数部分具有固定的位宽。因此,如果您只存储0到1之间的值,则这是不可能的(即使您总会有未使用的位)。 (参见:http://en.wikipedia.org/wiki/Single-precision_floating-point_format
您确定双精度浮点数的52位小数部分不够精确吗?
编辑:如果您使用浮点格式的整个范围,则在规范化值时会失去精度。四舍五入可能会出错,并且足够小的值将变为0。除非您知道这是一个问题,否则不要担心。否则,您必须查找其他答案中提到的其他解决方案。

问题在于知道最大值和最小值有多大,如果它们之间有足够的差异,双精度浮点数的52位部分可能会出现问题。这就是为什么我不想浪费类型中的任何位,以便我可以充分利用这64位的原因。 - user2485710
如果您拥有如此广泛的数据范围并且需要不丢失任何数据,那么这可能是一个问题。是否是这种情况?您可以使用http://www.cplusplus.com/reference/limits/numeric_limits/来检查是否低于最小值(= 0)。浮点除法几乎总是伴随着舍入误差。 - Moberg

2

2
如果你有一组双精度数,并将它们归一化到0.0到1.0之间,会存在一些精度丢失的情况。然而,这些情况都比你想象的要小得多。
首先,在规范化所需的算术操作中会因为舍入而失去一些精度。这相对较小,每个操作只有一个或几个位,并且通常是相对随机的。
其次,指数部分将不再使用正指数可能性。
第三,由于所有值都是正的,符号位也将被浪费。
第四,如果输入空间不包括+inf、-inf、+NaN、-NaN或类似的代码点,则这些代码点也将被浪费。
但是,总体来说,在归一化时你会浪费64位双精度数中约3位信息,其中一位是处理有限位宽值时几乎无法避免的。
任何从0到1的64位定点表示都比双精度数具有更少的“范围”。双精度数可以表示大约10^-300的东西,而包括1.0的64位定点表示只能达到大约10^-19左右。(64位定点表示可以将1 - 10^-19表示为与1不同的值,而双精度数则不能;但是64位定点值不能表示比2^-64更小的任何值,而双精度数可以)。
上面的一些数字是近似值,可能取决于舍入/确切格式。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接