假设您有一个float64_t
数字,其中包含一个任意值,并且您想知道所述数字是否可以安全地降为float32_t
,并且限制所产生的舍入误差不得超过给定的epsilon。
可能的实现可能如下所示:
float64_t before = 1.234567890123456789;
float64_t epsilon = 0.000000001;
float32_t mid = (float32_t)before; // 1.2345678806304931640625
double after = (float64_t)mid; // 1.2345678806304931640625
double error = fabs(before - after); // 0.000000009492963526369635474111
bool success = error <= epsilon; // false
为了让事情变得更有趣,让我们假设您不应该对这两种类型之间的值进行任何实际类型转换(如上所示)。
只是为了提升一个档次:让我们假设您不转向float32_t
,但浮点类型 任意精度(8位,16位,32位,甚至可能是24位)由其位数和指数长度指定(并遵循IEEE 754的约定,例如舍入为偶数)。
所以我正在寻找的是一种类似于此的通用算法:
float64_t value = 1.234567890123456789;
float64_t epsilon = 0.000000001;
int bits = 16;
int exponent = 5;
bool success = here_be_dragons(value, epsilon, bits, exponent); // false
举一个例子,将64位数字1.234567890123456789
向下压缩到较低的精度会导致以下舍入误差:
8bit: 0.015432109876543309567864525889
16bit: 0.000192890123456690432135474111
24bit: 0.000005474134355809567864525889
32bit: 0.000000009492963526369635474111
40bit: 0.000000000179737780214850317861
48bit: 0.000000000001476818667356383230
56bit: 0.000000000000001110223024625157
众所周知:
min
和max
值(可以从上面得出)。((2^exponent) - 2) * (2^mantissa)
)bias
((2^(exponent - 1)) - 1
)value
(以给定的更高精度类型提供)。epsilon
(也在给定的更高精度类型中提供)。(预期误差的近似值可能已足够,具体取决于其准确度和偏差因子。显然,确切的计算是合适的。)
无需涵盖的案例(因为它们可以单独解决):
true
。false
。< / LI>
到目前为止我已经想到了什么:
我们知道给定浮点类型中正正常值(不包括零)的计数,并且我们知道负值空间对称到积极一。
我们也知道离散值在值范围内(远离零)的分布遵循指数函数及其相对epsilon 相关的阶跃函数:
应该可以计算给定浮点类型实际值的nth
离散正态值 >会落到(通过某种对数投影,还是什么?),不应该吗?鉴于此n
,应该能够从步骤函数计算相应值的epsilon ,并将其与指定的最大错误进行比较,不是吗?
我觉得这实际上足以计算(或至少准确估计)预期的铸造误差。我根本不知道如何把这些东西放在一起。
你会怎么做? (实际代码的奖励积分:P)
Ps:提供更多上下文:我正在进行var_float
实现,以便找出给定值的最小无损(或给定epsilon内的有损)可转换表示我目前正在利用上述天真的往返逻辑执行二进制搜索,以找到合适的大小。它有效,但缺乏效率和凉爽部门。即使它绝不是性能瓶颈(yada yada早熟优化yada yada),我很好奇是否可以找到更加数学基础和优雅的解决方案。 ;)子>
答案 0 :(得分:4)
以下内容可能有效:
double isbad(double x, double releps) {
double y = x * (1 + 0x1.0p29);
double z = y-x-y+x;
return !(fabs(z/x) < releps);
}
这使用了一个技巧(由于Dekker,我相信)将浮点数分成“大半”和“小半”,它们与原始数字完全相加。我希望“大半”有23位而“小半”有其余的,所以我用常数1 + 2 ^(52-23)分割。
注意事项:您需要通过检查上限和下限来处理更有限的指数范围。次正规(特别是小型但不是大型的结果是低于正常的情况)需要不同的特殊处理。我写了!(fabs(z/x) < releps)
而不是fabs(z/x <= releps
因为我希望NaN符合“坏”的条件。 releps
是该变量的错误名称,因为阈值实际上比使用舍入到最近时指定的数字大半个ulp。
答案 1 :(得分:2)
向下转换相当于将尾数的最低有效位设置为零。
因此,对于给定的浮点数,只需提取尾数的最低有效位(宽度取决于向下转换类型)并使用当前指数进行缩放。这应该(非常精确地)是在向下转换中发生的“舍入误差”。
<小时/> 的修改
如上述评论所述,上述情况仅适用于所有案例的50%。 (当向下转向导致向下舍入时)。如果向下转向导致四舍五入,稍微修改的方法将有所帮助:
(极端/极端情况:示例:下调类型的五位尾数)
Rounding down: 0x1.00007fff -> 0x1.0000
-> Err == 0x0.00007fff
Rounding up: 0x1.00008000 -> 0x1.0001 -> Err == 0x1.00010000 - 0x1.00008000
-> Err == 0x0.00008000