浮点数小数部分精度

4

float1.0f0.0f 之间的精度有多少位,以使每个值都能被唯一表示?

例如,如果第一个小数 float 无法表示 0.13f,则答案是 float 只有 1 位精度。


3
你的第一句话暗示你正在寻找独特的表示方法,而第二句则显示你希望精确地表示。你到底需要哪种?我无法完全准确地表示0.1,但我可以提供它的唯一表示方法。 - Joseph Mansfield
请阅读 http://floating-point-gui.de/。 - Basile Starynkevitch
@JosephMansfield 在0.0和1.0之间有无限多个实数。每个具有2位精度的数字都可以转换为“int”并返回,以便保留原始的“float”吗?因此,在示例问题中,如果我可以表示:0.00F0.01F0.02F0.03F0.04F0.05F0.06F0.07F0.08F0.09F0.10F0.11F0.12F0.14F,那么我就不能唯一地表示0.13F。这意味着如果我执行以下操作:float foo = 0.13F; foo将与0.12F0.14F具有相同的表示形式。 - Jonathan Mee
2个回答

7
std::numeric_limits<float>::digits10

来自http://en.cppreference.com/w/cpp/types/numeric_limits/digits10 标准的32位IEEE 754浮点数类型有24位小数部分(写了23位,一个隐含的),这可能表明它可以表示7位数字(24 * std::log10(2)为7.22)。但是,相对舍入误差是不均匀的,并且一些带有7个小数位的浮点值在转换为32位浮点数后无法保留:最小的正例子是8.589973e9,在往返后变为8.589974e9。这些舍入误差不能超过表示中的一位,digits10的计算公式为(24-1)*std::log10(2),为6.92。向下取整得到6的值。
编辑2: 这表明任何浮点数都不是7位数字,而只有6位数字,就像std::numeric_limits<float>::digits10函数所示。
float orgF = 8.589973e9;
int i = orgF;
float f = i;
assert(f == orgF);

当进行往返转换时,此项将会失败,因为该过程会更改该值。

因此,如果我们只寻找介于1.0和0.0之间的数字,则答案为7,因为最小的正数存在问题是8.589973e9。


@Surt,我无法理解这个语句,你能给我解释一下吗?“但相对舍入误差是不均匀的,一些具有7位小数的浮点值无法在转换为32位浮点数后再转回去。” 我猜这是指早期的声明:“任何具有这么多小数位数的数字都可以被转换为类型T的值,并且在舍入或溢出方面不会发生变化。” - Jonathan Mee
1
往返转换 float orgF = 8.589973e9;int i = orgF; float f= i; assert(f == orgF); 可能出错。 - Surt
示例添加的内容与OP的问题没有直接关系。 - Mohit Jain
该示例是针对原帖评论中的问题而提供的。 - Surt
我的观点是:相对舍入误差是不均匀的,OP想要询问 在1.0f和0.0f之间的浮点数,而这个例子讨论了一个范围为8589973000的数字。这如何证明(我不是在反驳并且相信你的答案6是正确的和有良好的参考)在范围1.0f到0.0f中真实的数字相同?在您的编辑之后,您需要更详细地说明有效数字的数量取决于指数。 - Mohit Jain

4
如果我理解你的问题正确,答案是6。
#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
using namespace std;

int main() {
    int i = 10;  /* Number of distinct number after 1 place of decimal */
    int k = 1;   /* Number of decimal places */
    for(;/* ever */;)
    {
        float prev = 0.0f;
        for(int j = 1; j <= i; ++j)
        {
            stringstream ss;
            ss << "0.";  /* Prepare stream with 0. followed by k digit number with leading zeroes */
            ss << setfill('0') << setw(k) << j;
            float next; /* Read the number */
            ss >> next;
            if(prev == next) return 0;  /* If previous number and current number can not be distinguished */
            prev = next;
        }
        cout << "Works for " << k << " places" << endl;
        i *= 10; /* 10 times more tests to be conducted for 1 more decimal places */
        k++;     /* Try for more decimal places */
    }
    return 0;
}

代码的功能是什么

1. 设置小数点后一位的精度。
2. 将0.0与0.1、0.1与0.2……0.8与0.9以及0.9与1.0进行比较,如果其中任意一对相等(无法区分),则退出。否则,打印“适用于1位”。
3. 将小数点后两位的精度设置为2。
4. 比较0.00与0.01、0.01与0.02……0.98与0.99以及0.99与1.00,如果其中任意一对相等(无法区分),则退出。否则,打印“适用于2位”。
5. 除非退出,否则重复类似的步骤来处理3位及更多位数字。

+1 这是一个非常实用的回答。我希望我能接受这个作为答案,但似乎 @Surt 的答案更好,正如你所说的“写得很好”。 - Jonathan Mee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接