c – 查找浮点计数器的最大值

如果以前曾经问过我,我很抱歉,但我找不到它.

我想知道是否有办法计算用作计数器的单精度浮点数达到’最大值’的点(由于丢失而无法再添加其他值的点)精确).

例如,如果我连续向浮点数添加0.1f,我将最终到达值不会改变的点:

const float INCREMENT = 0.1f;
float value = INCREMENT;
float prevVal = 0.0f;

do {
  prevVal = value;
  value += INCREMENT;
} while (value != prevVal);

cout << value << endl;

在海湾合作委员会,这输出2.09715e 06

有没有办法以数学方式计算INCREMENT的不同值?我认为理论上它应该是当浮点数的指数部分需要超过23位的移位时,导致丢失尾数并简单地加0.

解决方法

给定一些正y用作增量,添加y不产生大于X的结果的最小X是2的最小幂,不小于y除以浮点格式的“epsilon”的一半.它可以通过以下方式计算:
Float Y = y*2/std::numeric_limits<Float>::epsilon();
int e;
std::frexp(Y,&e);
Float X = std::ldexp(.5,e);
if (X < Y) X *= 2;

证据如下.我假设IEEE-754二进制浮点运算使用round-to-nearest-ties-even.

当在IEEE-754浮点运算中添加两个数字时,结果是精确的数学结果四舍五入到所选方向上最接近的可表示值.

关于表示法的注释:源代码格式的文本表示浮点值和操作.其他文字是数学的.因此,x y是x和y的精确数学和,x是浮点格式的x,x y是在浮点运算中添加x和y的结果.另外,我将使用Float作为C中的浮点类型.

给定浮点数x,考虑使用浮点算法x y添加正值y.在什么条件下结果会超过x?

令x1为浮点格式中可表示的大于x的下一个值,并让xm为x和x1之间的中点.如果x y的数学值小于x m,则浮点计算x y向下舍入,因此它产生x.如果x y大于xm,则它向上舍入并产生x1,或者它产生更大的数字,因为y足够大以将总和移动到x1以上.如果x y等于xm,则结果为x或x1中的任何一个具有偶数低位.由于我们将看到的原因,在与此问题相关的情况下,这总是x,因此计算向下舍入.

因此,当且仅当x y超过xm时,x y产生大于x的结果,这意味着y超过从x到x1的距离的一半.注意,从x到x1的距离是x的有效数字的低位数中的值1.

在其有效数字中具有p位的二进制浮点格式中,低位的位置值是高位数的位置值的21-p倍.例如,如果x是2e,则其有效数中的最高位表示2e,最低位表示2e 1-p.

问题是,给定一个y,x y不会产生大于x的结果的最小x是多少?它是x的最小值,y不超过x的有效数字的低位数值的一半.

设2e为x的有效位的高位的位置值.那么y≤½•2e 1-p = 2e-p,所以y•2p≤2e.

因此,给定一些正y,x y不产生大于x的结果的最小x具有其前导位2e,等于或超过y·2p.实际上它必须正好是2e,因为其前导位具有位置值2e的所有其他浮点数在其有效数中设置了其他位,因此它们更大. 2e是前导位代表2e的最小数.

因此,x是2的最小幂,等于或超过y•2p.

在C中,std :: numeric_limits< Float> :: epsilon()(来自< limits>标头)是从1到下一个可表示值的步骤,意味着它是21-p.所以y•2p等于y * 2 / std :: numeric_limits< Float> :: epsilon(). (除非溢出到∞,否则此操作是准确的.)

让我们将它赋给变量:

Float Y = y*2/std::numeric_limits<Float>::epsilon();

我们可以通过使用frexp(来自< cmath>标头)从Y和ldexp(也是< cmath>)的浮点表示中提取指数来找到由Y的有效数字的最高位表示的位置值,以应用该值exponent到一个新的有效数字(.5因为frexp和ldexp使用的比例):

int e;
std::frexp(Y,e);

那么X是2的幂,它小于或等于Y.它实际上是2的最大幂不大于Y,因为2的2X的下一个更大的幂大于Y.但是,我们想要两个不小于Y的最小功率.我们可以找到:

if (X < Y) X *= 2;

得到的X是问题所寻求的数字.

相关文章

/** C+⬑ * 默认成员函数 原来C++类中,有6个默认成员函数: 构造函数 析构函数 拷贝...
#pragma once // 1. 设计一个不能被拷贝的类/* 解析:拷贝只会放生在两个场景中:拷贝构造函数以及赋值运...
C类型转换 C语言:显式和隐式类型转换 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译...
//异常的概念/*抛出异常后必须要捕获,否则终止程序(到最外层后会交给main管理,main的行为就是终止) try...
#pragma once /*Smart pointer 智能指针;灵巧指针 智能指针三大件//1.RAII//2.像指针一样使用//3.拷贝问...
目录&lt;future&gt;future模板类成员函数:promise类promise的使用例程:packaged_task模板类例程...