说单精度碰撞的第一个数字是 131072.02 是否正确?(阳性,将 2 位数字视为尾数)

Is it correct to state that the first number that collide in single precision is 131072.02? (positive, considering 2 digits as mantissa)

本文关键字:数字 阳性 碰撞 精度 单精度 第一个 是否 131072      更新时间:2023-10-16

我试图为我的音频应用程序弄清楚它是否可以用于正确表示我将使用的参数范围float

它需要的"最大"掩码是频率参数,这是正数,并且允许最多两位数作为尾数(即从 20.00Hz 至 22000。00赫兹)。从概念上讲,以下数字将被四舍五入,所以我不在乎它们。

所以我做了这个脚本来检查第一个以单精度碰撞的数字:

float temp = 0.0;
double valueDouble = 0.0;
double increment = 1e-2;
bool found = false;
while(!found) {
double oldValue = valueDouble;
valueDouble += increment;
float value = valueDouble;
// found
if(temp == value) {
std::cout << "collision found: " << valueDouble << std::endl;
std::cout << "   collide with: " << oldValue << std::endl;
std::cout << "float stored as: " << value << std::endl;
found = true;
}
temp = value;        
}

它似乎131072.02(带有131072.01,存储为相同的131072.015625值),远于22000.00。看来我可以使用浮点数。

但我想了解这种推理是否正确。是的?

整个问题是,如果我设置一个 XXXXX.YY (7 digits) 的参数,并且它与其他一些位数较少的数字发生冲突(因为single precision只能保证6 digits)

注意:当然,诸如1024.00029981459101691143587231636047363281251024.000199814591042013489641249179840087890625之类的数字会碰撞,并且它们在间隔内,但它们的表示数字比我要求的尾数更长,所以我不在乎。

IEEE 754 单精度定义为

  • 1 个符号位
  • 8 个指数位:范围 2^-126 到 2^127 ~ 10^-38 到 10^38)
  • 23 分数(尾数)位:十进制精度取决于指数)

在 22k 处,指数将表示 16384=2^14 的偏移量,因此 23 位尾数将为您提供 2^14/2^23= 1/2^9 = 0.001953125 的精度......这对您的情况来说已经足够了。

对于 131072.01,指数将表示偏移量 131072 = 2^17,因此尾数将给出 2^17/2^23 = 1/2^6 = 0.015625 的精度,这比您的目标精度 0.01 大

你的程序没有准确地验证你想要什么,但你的基本推理应该是可以的。

该程序的问题在于 valueDouble 会累积轻微的错误(因为 0.01 不能准确表示) - 将字符串"20.01"转换为浮点数将引入轻微的舍入误差。

但是这些错误应该在DBL_EPSILON量级上,并且比您看到的错误小得多。

如果你真的想测试它,你必须写"20.00"到"22000.00",并使用你计划使用的scanf变体扫描它们,并验证它们是否不同。

声明以

单精度碰撞的第一个数字是 131072.02 是否正确?(正数,考虑小数点后 2 位数字作为尾数)

是的。


我想了解这个推理是否正确。是的?

对于仅小于 131072.0f 的值,每个连续的可表示float值相距 1/128。

对于 [131072.0f ...2*131072.0f),每个连续的可表示float值相距 1/64。

对于十进制文本形式"131072.xx"的值,有 100 种组合,但只有 64 种不同float。 发生 100-64 或 36 次碰撞并不奇怪 - 见下文。 对于这种形式的数字,这是float密度太稀疏的第一个地方:在这个范围内float> 0.01 中的最小有效位。


int main(void) {
volatile float previous = 0.0;
for (long i = 1; i <= 99999999; i++) {
volatile float f1 = i / 100.0;
if (previous == f1) {
volatile float f0 = nextafterf(f1, 0);
volatile float f2 = nextafterf(f1, f1 * 2);
printf("%f %f %f    delta fraction:%fn", f0, f1, f2, 1.0 / (f1 - f0));
static int count = 100 - 64;
if (--count == 0) return 0;
}
previous = f1;
}
printf("Donen");
}

输出

131072.000000 131072.015625 131072.031250    delta fraction:64.000000
131072.031250 131072.046875 131072.062500    delta fraction:64.000000
131072.046875 131072.062500 131072.078125    delta fraction:64.000000
...
131072.921875 131072.937500 131072.953125    delta fraction:64.000000
131072.937500 131072.953125 131072.968750    delta fraction:64.000000
131072.968750 131072.984375 131073.000000    delta fraction:64.000000

为什么浮点数的有效数字是 7 或 6 也可能有所帮助。