说明以单精度碰撞的第一个数字是131072.02是否正确? (正面,考虑2位数作为尾数)

时间:2018-04-11 09:45:17

标签: c++ floating-point

如果可以使用float正确表示我将使用的参数范围,我试图找出我的音频应用程序。

它所需的“最大”掩模是频率参数,它是正的,并且允许最多两位数作为尾数(即从20. 00 hz到22000. 00 hz)。从概念上讲,以下数字将被四舍五入,所以我不关心它们。

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

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.02131072.01,存储为相同的131072.015625值),远远超过22000.00。似乎我可以使用浮动。

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

如果我设置一个XXXXX.YY(7 digits)的参数并且它与其他一些数字较少的数字发生冲突(因为single precision仅保证6 digits,整个问题就出现了。 })

注意:当然1024.00029981459101691143587231636047363281251024.000199814591042013489641249179840087890625之类的数字会发生碰撞,并且它们在这个区间内,但它们的数字长度超过我所需的尾数,所以我不在乎

3 个答案:

答案 0 :(得分:3)

IEEE 754 Single precision定义为

  • 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

答案 1 :(得分:1)

您的程序无法准确验证您的需求,但您的基本推理应该没问题。

该程序存在的问题是valueDouble会累积一些小错误(因为0.01并未准确表示) - 并转换字符串&#34; 20.01&#34;浮点数会引入轻微的舍入错误。

但是这些错误应该是DBL_EPSILON的顺序,并且比你看到的错误要小得多。

如果你真的想测试它,你必须写&#34; 20.00&#34;至&#34; 22000.00&#34;并使用您计划使用的scanf-variant扫描它们并验证它们是否不同。

答案 2 :(得分:1)

  

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

  

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

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

对于[131072.0f ... 2 * 131072.0f]范围内的值,每个连续可表示的float值相差1/64。

使用十进制文本格式&#34; 131072.xx&#34;的值,有100种组合,但只有64种不同float。发生100-64或36 collisions并不奇怪 - 见下文。对于此形式的数字,这是float的密度太稀疏的第一个位置:float中的最低有效位&gt;在此范围内为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:%f\n", f0, f1, f2, 1.0 / (f1 - f0));
      static int count = 100 - 64;
      if (--count == 0) return 0;
    }
    previous = f1;
  }
  printf("Done\n");
}

输出

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

Why floating-points number's significant numbers is 7 or 6也可以提供帮助。