C++ 第一个以单精度碰撞的数字是131072.02,这是正确的吗?(正,考虑2位数字作为尾数)
我试图为我的音频应用程序找出C++ 第一个以单精度碰撞的数字是131072.02,这是正确的吗?(正,考虑2位数字作为尾数),c++,floating-point,C++,Floating Point,我试图为我的音频应用程序找出float是否可以用来正确表示我将使用的参数范围 它需要的“最大”掩码用于频率参数,该参数为正,并允许最大两位数作为尾数(即从20.00 hz到22000.00 hz)。从概念上讲,以下数字将四舍五入,因此我不喜欢它们 所以我做这个来检查第一个以单精度碰撞的数字: float temp = 0.0; double valueDouble = 0.0; double increment = 1e-2; bool found = false; while(!found)
float
是否可以用来正确表示我将使用的参数范围
它需要的“最大”掩码用于频率参数,该参数为正,并允许最大两位数作为尾数(即从20.00 hz到22000.00 hz)。从概念上讲,以下数字将四舍五入,因此我不喜欢它们 所以我做这个来检查第一个以单精度碰撞的数字:
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;
}
float temp=0.0;
double-valueDouble=0.0;
双增量=1e-2;
bool-found=false;
而(!found){
double oldValue=valueDouble;
valueDouble+=增量;
浮动值=双精度值;
//发现
如果(温度==值){
标准::cout定义为
- 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_ε的量级上,并且比您看到的误差小得多
如果您真的想测试它,您必须将“20.00”写入“22000.00”,并使用您计划使用的scanf变体扫描它们,并验证它们是否不同
单精度冲突的第一个数字是131072.02,这是否正确?(正,考虑小数点后的两位数作为尾数)
对
我想知道这个推理是否正确,是吗
对于刚好小于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
可能也会有帮助。@JHBonarius你看到他在哪里抱怨?作为背景,请阅读以下内容:还有这些:(不是我的博客)“即从20.00 hz到22000.00 hz”-你应该使用的是一个以厘米为单位的整数。这个整数的范围是[20002200000]
,并且没有任何舍入问题。为什么?因为浮点值
甚至不能正确表示0.2
!“尾数最多两位数(即从20.00 hz到22000.00 hz)”.Eh,不,那不是两位数尾数。22000.00Hz
将是2.200000E5
,所以尾数是7位数,指数是一位数。除了计算机使用基数2和位数的小位,而不是基数10和位数。OP测试不一致的原因解释(累积误差)这将改善这个答案。@Yakk这就是你的意思吗?因为我们处理的是相对精度,所以没有必要处理问题中发现的不精确分数。瞧,使用24位,所有高达1600万的整数都可以用单精度浮点精确表示。因为OP只需要2200000厘赫兹,这就足够了“错误累积”并不重要,因为结果(如打印)说明了该错误。错误累积只意味着131072
不会在13107200
的精确步骤中达到。@MSalters:错误累积确实重要(一般来说,在这种情况下不一定与特定的数字相关)。这意味着valueDouble
中的值将不是OP打算测试的数字。理论上,可能存在某个点,测试的数字应该是a和b,但错误会导致测试a+e0和b+e1,而a和b冲突,而a+e0和b+e1不冲突,反之亦然,因此结果是错误的。冲突测试是d一个在打印任何内容之前,所以输出的转换对测试没有影响。@EricPostpischil:这没有意义。x
和x+0.01
发生碰撞,只要x+0.01
仅在第25位不同。回想加法的工作原理:最小的数字的指数调整为最大的数字,移动其mant右边是issa。只要移动25位,你就加上0,然后发生冲突。所有具有相同大小的数字x
都将导致相同的“移动25位,加上0”"@mAlters:首先,如果您有x
和x+.01
,它们之间的差异不超过.01,因为x+.01
在二进制浮点中必然有舍入错误。因此x
和x+0.01
可能在第25位不同,而x
和x
+.01在第25位没有差异。第二,即使这两种情况下e对x
和x+.01
和对x
和x
+.01在第25位不同,x
有一些累积误差,它取代了我们在程序中此时的理想y,y和y+.01在第25位可能没有差异,即使x
和x
+.0我同意,反之亦然。
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