【问题标题】:Some questions about floating points关于浮点数的一些问题
【发布时间】:2010-10-06 16:10:45
【问题描述】:

我想知道一个数字是否在浮点表示中以一种方式表示,它是否会在更大尺寸的表示中以相同的方式表示。 也就是说,如果一个数字具有float 的特定表示,如果将float 转换为double,然后转换为long double 时仍然相同,它是否具有相同的表示。

我想知道,因为我正在编写一个 BigInteger 实现,并且传入的任何浮点数都发送到一个接受 long double 的函数以进行转换。这引出了我的下一个问题。显然浮点并不总是有精确的表示,所以在我的 BigInteger 类中,当给定浮点数时我应该尝试表示什么。尝试表示与std::cout << std::fixed << someFloat; 给出的相同数字是否合理,即使这与传入的数字不同。这是我能得到的最准确的表示吗?如果是这样,...

提取该值的最佳方法是什么(以 10 的幂为底),目前我只是将它作为字符串抓取并将其传递给我的字符串构造函数。这会奏效,但我不禁觉得有更好的方法,但用浮点数除以我的基数时肯定取余数是不准确的。

最后,我想知道是否存在与uintmax_t 等效的浮点数,即类型名将始终是系统上最大的浮点类型,还是没有意义,因为long double 将始终是最大的(即使它与双精度相同)。

谢谢,T。

【问题讨论】:

    标签: c++ floating-point floating-accuracy


    【解决方案1】:

    如果“相同的表示”是指“内存中除了填充之外完全相同的二进制表示”,那么不是。双精度具有更多的指数和尾数位,并且具有不同的指数偏差。但我相信任何单精度值都可以用双精度精确表示(可能是非规范化值除外)。

    当您说“浮点数并不总是具有精确表示”时,我不确定您的意思。当然,并非所有十进制浮点值都具有精确的二进制浮点值(反之亦然),但我不确定这是否是个问题。只要您的浮点输入没有小数部分,那么适当大的“BigInteger”格式就应该能够准确地表示它。

    通过 base-10 表示的转换不是要走的路。理论上,您只需要一个长度约为 1024 的位数组,将其全部初始化为零,然后将尾数位移入指数值。但如果不了解更多关于您的实施的信息,我无法提供更多建议!

    【讨论】:

    • 感谢您的回答。当您说“[...] BigInteger 格式应该能够准确地表示它”时,我不确定我是否理解您的意思。当然 BigInteger 应该能够表示它,但我如何首先获得价值。编译器接受一些数字(没有小数部分)作为有效的浮点数,但是当我打印它们时,我得到一个不同的数字。这是cout 的问题吗,数字仍然准确表示。对不起,这有点不连贯,我对此有点困惑。另外,如果我发布我的大纲会有所帮助...
    • ... 实现,我很乐意这样做。虽然有点长。 (目前非常粗糙)。
    • 我假设你的意思是float f = 123456789123456789.0f; 这是浮点的限制,而不是“BigIntegers”。 BigIntegers 应该能够表示浮点数的所有可能(整数)值,但反之则不然。
    • 是的,这就是我的意思。所以,当我收到这样一个浮点数时,我认为数据丢失已经发生了。现在是我能得到的最准确的表示,即cout 上显示的那个数字,如果是,尝试在我的 BigInteger 类中表示相同的数字是否合理。当我说合理时,​​我的意思是与 c++ 处理这些事情的方式保持一致,有没有比简单地将它传递给我的字符串构造函数更好的方法来获取该数字。
    • 忽略 cout 向您显示的内容,因为它基于各种格式选项。我上面的建议(转移到一个巨大的位数组中)在逻辑上是你需要做的;我将把实际的实现留给你。不过,freexp 函数 (cplusplus.com/reference/clibrary/cmath/frexp) 可能对您有用。
    【解决方案2】:

    double 包括float 的所有值; long double 包括 double 的所有值。因此,您不会因为转换为long double 而丢失任何价值信息。但是,您会丢失有关原始类型的相关信息(见下文)。

    为了遵循常见的 C++ 语义,将浮点值转换为整数应该截断该值,而不是舍入。

    主要问题在于不精确的大值。您可以使用frexp 函数查找浮点值的以 2 为底的指数。您可以使用std::numeric_limits<T>::digits 来检查它是否在可以精确表示的整数范围内。

    我个人的设计选择是断言 fp 值在可以精确表示的范围内,即限制任何实际参数的范围。

    要正确执行此操作,您需要采用 floatdouble 参数的重载,因为可以表示的范围完全取决于实际参数的类型。

    当您的 fp 值在允许范围内时,您可以使用 floorfmod 来提取您想要的任何数字系统中的数字。

    【讨论】:

    • +1。虽然我不同意你的设计选择。它违背了自然扩展 C++ 语义的想法。如果值(截断后)适合 64 位,我当然可以将带有 24 位尾数的 32 位浮点数分配给 64 位整数。
    • 我也+1,感谢您的回复,这很有帮助。不幸的是,我真的不想走人为限制值范围的路线,如果 c++ 接受它,我愿意接受它,我只是不确定我应该接受什么值!
    • floor(abs(v)),符号为原始值v。我建议不接受“不精确”值的原因是任意或扩展精度整数算术的点通常是为了得到精确的结果。我应该添加一个“默认情况下”的黄鼠狼短语,我的意思是,为什么不同时支持两者? :-)
    【解决方案3】:

    是的,例如,从 IEEE 浮点数到双精度数到扩展,您会看到从较小格式到较大格式的位,例如

    单身的 S EEEEEEEE 嗯嗯嗯...... 双倍的 S EEEEEEEEEEEE 嗯…… 6.5单 0 10000001 101000... 6.5双 0 10000000001 101000... 13单 0 10000010 101000... 13双 0 10000000010 101000...

    您将留下的尾数对齐,然后添加零。

    指数是右对齐的,将符号扩展到 msbit 旁边,然后复制 msbit。

    例如 -2 的指数。取-2减去1,即-3。二进制补码中的 -3 是 0xFD 或 0b11111101,但格式中的指数位是 0b01111101,msbit 反转。对于双 -2 指数 -2-1 = -3。或 0b1111...1101 变为 0b0111...1101,msbit 反转。 (指数位 = twos_complement(exponent-1) 与 msbit 反转)。

    正如我们在上面看到的,指数 3 3-1 = 2 0b000...010 反转高位 0b100...010

    所以是的,您可以从单精度中获取位并将它们复制到双精度数中的正确位置。我没有方便的扩展浮点引用,但很确定它的工作方式相同。

    【讨论】:

    • 即在 IEEE 754 之类的浮点格式内。例如,如果您想从 IEEE 754 转换为 TI dsp 格式,则不能那样工作,无法复制位。通常,尽管在同一标准内,各种精度将尾数更多地向右扩展,指数向左扩展,从而增加了更多精度,而无需重新定义它们的工作方式。
    猜你喜欢
    • 2020-09-18
    • 2014-11-22
    • 1970-01-01
    • 2022-11-15
    • 1970-01-01
    • 1970-01-01
    • 2018-05-24
    • 2013-07-07
    • 2013-05-10
    相关资源
    最近更新 更多