代码之家  ›  专栏  ›  技术社区  ›  Mark T

下一个高/低IEEE双精度数

  •  19
  • Mark T  · 技术社区  · 16 年前

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

    难点在于IEEE格式并不完全一致。如果使用低级代码并实际将一个添加到最低有效位,则生成的格式可能不是下一个可用的double。例如,它可能是一个特殊的大小写编号,例如PositiveInfinity或NaN。还有一些次正常值,我不知道,但似乎有不同于“正常”模式的特殊位模式。

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

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

    是否有方法获取下一个值(不需要某种试图添加越来越小的值的循环)。

    6 回复  |  直到 13 年前
        1
  •  13
  •   F'x    16 年前

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

    • 如果你有一个像样的C99数学库,你可以使用 nextafter (以及它的浮动和长双变量, nextafterf nextafterl );或 nexttoward 家庭(以长的双倍作为第二个论点)。

    • 如果你写Fortran,你有 nearest 固有可用

    如果不能直接从您的语言访问它们,您还可以查看它们是如何在免费提供的环境中实现的,例如 this one .

        2
  •  8
  •   Jim    13 年前

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

    对于32位和64位浮点运算的用户来说,正确理解基本结构对于避免使用它们带来的一些危险非常有用。IEEE标准的应用是一致的,但仍有许多细节有待于实现者。因此,基于机器字表示的位操作的平台通用解决方案可能有问题,并且可能依赖于诸如endian等问题。虽然了解它如何能够或应该在位级别工作的所有血腥细节可能显示出智能能力,但最好使用为每个平台量身定制的内部或库解决方案,并且在支持的平台上具有通用API。

    我注意到了C和C++的解决方案。以下是一些Java:

    数学.nextUp:

    公共静态double nextUp(double d):

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

    特殊情况:

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

    参数:

    • d-起始浮点值

    返回:

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

    公共静态float nextUp(float f):

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

    特殊情况:

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

    参数:

    • f-起始浮点值

    返回:

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

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

    数学.nextAfter:

    public static double nextAfter(双启动, 双向)

    • 返回中第一个参数旁边的浮点数 第二个参数的方向。如果两个参数比较为 等于返回第二个参数。

    特殊情况:

    • 如果两个参数都是NaN,则返回NaN。
    • 如果两个参数都有符号零,则返回方向不变 (根据返回第二个参数的要求,如果 参数比较为相等)。
    • 如果起始值为±Double.MIN_,并且方向的值为 结果应该是一个较小的量值,然后是一个相同的0 返回开始时签名。
    • 如果start是无限的,并且direction的值是 应该有一个较小的幅值,Double.MAX_值,符号相同 当开始返回时。
    • 如果start等于±Double.MAX_值,并且direction有一个值 结果应该有一个更大的量,一个无穷大 返回与start相同的符号。

    参数:

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

    返回:

    • 与起始方向相邻的浮点数 方向。

    公共静态浮点nextAfter(浮点开始, 双向)

    • 返回中第一个参数旁边的浮点数 第二个参数的方向。如果两个参数比较为 等于返回第二个参数的值。

    特殊情况:

    • 如果两个参数都是NaN,则返回NaN。
    • 如果两个参数都是带符号的零,则等于方向的值 被退回。
    • 如果start是“Float.MIN”,则方向的值为 结果应该是一个较小的量值,然后是一个相同的0 返回开始时签名。
    • 如果start是无限的,并且direction的值是 应该有一个较小的值Float.MAX_,符号相同 当开始返回时。
    • 如果start等于Float.MAX,并且direction有一个这样的值 结果应该有一个更大的量,一个无穷大 返回开始时签名。

    参数:

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

    返回:

    • 与开始方向相邻的浮点数。
        3
  •  5
  •   phuclv    8 年前

    正如Thorsten S.所说,这可以通过 BitConverter 类,但他的方法假定 DoubleToInt64Bits 方法返回 double ,但事实并非如此。该方法返回的整数实际上返回0到您的可表示双精度数。一、 e.最小的正双精度表示为1,下一个最大的双精度表示为2等。负数从 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, 不过,如果你担心的话,你可以检查一下这些东西,想怎么处理就怎么处理。

        4
  •  2
  •   Thorsten S.    15 年前

    是的,有办法。 在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);
    }
    

    增加可以加上或减去。

        5
  •  1
  •   ire_and_curses    16 年前

    我不确定我在关注你的问题。当然是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等),它们定义得很好,而且没有很多。限制的定义类似。

    既然你已经调查过了,我想我找错了方向。如果这还不足以解决你的问题,你能试着澄清一下你想要达到的目标吗?你的目的是什么?

        6
  •  1
  •   Jim    13 年前

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

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

    推荐文章