27

我正在做高精度的科学计算。在寻找各种效果的最佳表示时,我不断提出想要获得下一个更高(或更低)双精度数的理由。本质上,我想做的是在双精度的内部表示中的最低有效位上加一个。

困难在于 IEEE 格式并不完全统一。如果要使用低级代码并实际上将 1 添加到最低有效位,则生成的格式可能不是下一个可用的双精度。例如,它可能是一个特殊情况数,例如 PositiveInfinity 或 NaN。还有一些低于正常值的值,我不声称理解,但它们似乎具有与“正常”模式不同的特定位模式。

可以使用“epsilon”值,但我从未理解它的定义。由于 double 值的间距不均匀,因此不能将单个值添加到 double 以产生下一个更高的值。

我真的不明白为什么 IEEE 没有指定一个函数来获得下一个更高或更低的值。我不能成为唯一需要它的人。

有没有办法获得下一个值(没有某种尝试添加越来越小的值的循环)。

4

7 回答 7

13

有一些功能可以做到这一点,但它们可能取决于您使用的语言。两个例子:

  • 如果您可以访问一个不错的 C99 数学库,则可以使用nextafter(以及它的 float 和 long double 变体,nextafterfand nextafterl);或nexttoward家庭(将 long double 作为第二个参数)。

  • 如果你写 Fortran,你就有nearest可用的内在函数

如果您不能直接从您的语言中访问这些,您还可以查看它们是如何免费实现的,例如这个.

于 2009-08-09T17:17:28.777 回答
9

大多数语言都有用于获取下一个或上一个单精度(32 位)和/或双精度(64 位)数字的内在函数或库函数。

对于 32 位和 64 位浮点运算的用户,对基本结构的良好理解对于避免它们的一些危险非常有用。IEEE 标准统一适用,但仍将许多细节留给实施者。因此,基于机器字表示的位操作的平台通用解决方案可能存在问题,并且可能取决于诸如字节序等问题。虽然了解它如何或应该如何在位级别工作的所有血腥细节可能会展示智力,但最好使用为每个平台量身定制并具有跨受支持平台的通用 API 的内在或库解决方案。

我注意到 C# 和 C++ 的解决方案。这里有一些Java:

Math.nextUp:

公共静态双下一个(双d):

  • 返回正无穷方向上与 d 相邻的浮点值。该方法在语义上等价于 nextAfter(d, Double.POSITIVE_INFINITY); 但是,nextUp 实现可能比其等效的 nextAfter 调用运行得更快。

特别案例:

  • 如果参数为 NaN,则结果为 NaN。
  • 如果参数是正无穷大,则结果是正无穷大。
  • 如果参数为零,则结果为 Double.MIN_VALUE

参数:

  • d - 起始浮点值

回报:

  • 接近正无穷大的相邻浮点值。

公共静态浮动 nextUp(float f):

  • 返回在正无穷大方向上与 f 相邻的浮点值。这个方法在语义上等价于 nextAfter(f, Float.POSITIVE_INFINITY); 但是,nextUp 实现可能比其等效的 nextAfter 调用运行得更快。

特别案例:

  • 如果参数为 NaN,则结果为 NaN。
  • 如果参数是正无穷大,则结果是正无穷大。
  • 如果参数为零,则结果为 Float.MIN_VALUE

参数:

  • f - 起始浮点值

回报:

  • 接近正无穷大的相邻浮点值。

接下来的两个使用起来有点复杂。然而,朝向零或朝向正无穷或负无穷的方向似乎更有可能和有用的用途。另一个用途是查看两个值之间存在的中间值。可以使用循环和计数器确定两个值之间存在多少个。此外,似乎它们以及 nextUp 方法可能对 for 循环中的递增/递减有用。

Math.nextAfter:

public static double nextAfter(双开始,双向)

  • 返回第二个参数方向上与第一个参数相邻的浮点数。如果两个参数比较相等,则返回第二个参数。

特别案例:

  • 如果任一参数是 NaN,则返回 NaN。
  • 如果两个参数都是带符号的零,则返回方向不变(如果参数比较相等,则返回第二个参数的要求暗示了这一点)。
  • 如果 start 是 ±Double.MIN_VALUE 并且 direction 的值使得结果应该具有较小的幅度,则返回与 start 具有相同符号的零。
  • 如果 start 是无限的,并且 direction 的值使得结果应该具有较小的幅度,则返回与 start 具有相同符号的 Double.MAX_VALUE。
  • 如果 start 等于 ± Double.MAX_VALUE 并且 direction 的值使得结果应该具有更大的量级,则返回与 start 符号相同的无穷大。

参数:

  • start - 起始浮点值
  • direction - 指示应该返回 start 的哪个邻居或 start 的值

回报:

  • 以方向相邻的浮点数开始的方向。

public static float nextAfter(浮动开始,双向)

  • 返回第二个参数方向上与第一个参数相邻的浮点数。如果两个参数比较相等,则返回与第二个参数等效的值。

特别案例:

  • 如果任一参数是 NaN,则返回 NaN。
  • 如果两个参数都是带符号的零,则返回与方向等效的值。
  • 如果 start 是 ±Float.MIN_VALUE 并且 direction 的值使得结果应该具有较小的幅度,则返回与 start 具有相同符号的零。
  • 如果 start 是无限的,并且 direction 的值使得结果应该具有较小的幅度,则返回与 start 具有相同符号的 Float.MAX_VALUE。
  • 如果 start 等于 ± Float.MAX_VALUE 并且 direction 的值使得结果应该具有更大的量级,则返回与 start 符号相同的无穷大。

参数:

  • start - 起始浮点值
  • direction - 指示应该返回 start 的哪个邻居或 start 的值

回报:

  • 以方向相邻的浮点数开始的方向。
于 2012-07-10T21:10:25.743 回答
6

正如 Thorsten S. 所说,这可以通过BitConverter类来完成,但他的方法假定该DoubleToInt64Bits方法返回 的内部字节结构double,但事实并非如此。该方法返回的整数实际上返回 0 和您的之间的可表示双精度数。即最小的正双精度由 1 表示,下一个最大的双精度是 2,等等。负数从 0d 开始long.MinValue并远离 0d。

所以你可以做这样的事情:

public static double NextDouble(double value) {

    // Get the long representation of value:
    var longRep = BitConverter.DoubleToInt64Bits(value);

    long nextLong;
    if (longRep >= 0) // number is positive, so increment to go "up"
        nextLong = longRep + 1L;
    else if (longRep == long.MinValue) // number is -0
        nextLong = 1L;
    else  // number is negative, so decrement to go "up"
        nextLong = longRep - 1L;

    return BitConverter.Int64BitsToDouble(nextLong);
}

这不涉及,Infinity但是NaN,如果您担心的话,您可以检查这些并按您喜欢的方式处理它们。

于 2010-02-17T19:02:47.957 回答
3

是的,有办法。在 C# 中:

       public static double getInc (double d)
        {
                // Check for special values
                if (double.IsPositiveInfinity(d) || double.IsNegativeInfinity(d))
                    return d;
                if (double.IsNaN(d))
                    return d;

                // Translate the double into binary representation
                ulong bits = (ulong)BitConverter.DoubleToInt64Bits(d);
                // Mask out the mantissa bits
                bits &= 0xfff0000000000000L;
                // Reduce exponent by 52 bits, so subtract 52 from the mantissa.
                // First check if number is great enough.
                ulong testWithoutSign = bits & 0x7ff0000000000000L;
                if (testWithoutSign > 0x0350000000000000L)
                  bits -= 0x0350000000000000L;
                else
                  bits = 0x0000000000000001L;
                return BitConverter.Int64BitsToDouble((long)bits);
}

增加的部分可以加减。

于 2009-12-08T00:04:52.560 回答
1

我不确定我是否在关注您的问题。IEEE 标准肯定完全统一的吗?例如,查看wikipedia article中的双精度数字摘录。

3ff0 0000 0000 0000   = 1
3ff0 0000 0000 0001   = 1.0000000000000002, the next higher number > 1
3ff0 0000 0000 0002   = 1.0000000000000004

在二进制或十六进制表示中仅增加最低有效位有什么问题?

就特殊数字而言(无穷大、NaN 等),它们的定义很明确,而且数量并不多。限制的定义类似。

既然您显然已经对此进行了研究,我想我弄错了。如果这不足以解决您的问题,您能否尝试澄清您想要实现的目标?你在这里的目的是什么?

于 2009-08-07T17:37:24.770 回答
1

关于 epsilon 函数,它是对二进制 double 可能与十进制值的近似值相差多远的估计。这是因为,对于非常大的正数或负数十进制数或非常小的正数或负数十进制数,它们中的许多映射到与双精度数相同的二进制表示。尝试一些非常非常大或非常非常小的十进制数,从它们创建双精度数,然后转换回十进制数。你会发现你不会得到相同的十进制数字,而是取而代之的是双精度数最接近的那个。

对于接近 1 或 -1 的值(相对于双精度可以表示的大量十进制值),epsilon 将为零或非常非常小。对于逐渐趋向 + 或 - 无穷大或零的值,epsilon 将开始增长。在值非常接近零或无穷大时,epsilon 将非常大,因为这些范围内十进制值的可用二进制表示非常非常稀疏。

于 2012-07-11T09:00:06.770 回答
1

C# 现在具有 .Net Core 3.0+ 的System.Math.BitIncrement/BitDecrement函数。

根据微软的说明

[这些对应]nextUpnextDownIEEE 操作。它们返回比较大于或小于输入的最小浮点数(分别)。例如,Math.BitIncrement(0.0)将返回double.Epsilon.

于 2021-11-04T14:50:11.583 回答