【问题标题】:What's the closest double to 1.0, that isn't 1.0?最接近 1.0 的双精度数是多少,不是 1.0?
【发布时间】:2016-12-12 14:47:01
【问题描述】:

有没有办法以编程方式获取最接近 1.0 但实际上不是 1.0 的双精度数?

实现此目的的一种巧妙方法是将双精度数 memcpy 为相同大小的整数,然后减一。 IEEE754 浮点格式的工作方式,最终将指数减一,同时将小数部分从全零 (1.000000000000) 更改为全一 (1.111111111111)。但是,有些机器将整数存储在小端,而浮点存储在大端,所以这并不总是有效。

【问题讨论】:

  • 您不能假设 +1 与 -1 的距离(从 1.0 开始)相同。以 10 为基数和以 2 为基数的浮点表示的交错意味着间隙是不均匀的。
  • @Richard:你是对的。减去一个 ULP 不太可能得到呃,“nextbefore”值,因为我猜指数也必须调整。 nextafter() 是实现他想要的唯一正确方法。
  • 仅供参考,我读过这个博客(不是我的):exploringbinary.com/…
  • @RudyVelthuis:它适用于每一种 IEEE754 二进制浮点格式。
  • 好的,然后告诉我:什么“适用于每种 IEEE754 浮点格式”?如果您减少有效数字,您将获得“firstbefore()”值,这根本是不正确的,尤其是对于 1.0,它的有效数字是 2 的幂。这意味着 1.0000... 二进制递减到 0.111111.... 并且要对其进行规范化,您必须将其向左移动:1.11111... 这需要您递减指数。然后你距离 1.0 2 ulp。所以不,从积分值中减去 1 并不能得到这里所要求的内容。

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


【解决方案1】:

从 C++11 开始,您可以使用nextafter 来获取给定方向的下一个可表示值:

std::nextafter(1., 0.); // 0.99999999999999989
std::nextafter(1., 2.); // 1.0000000000000002

Demo

【讨论】:

  • 这也是一种将双精度值递增到下一个可表示整数的好方法:std::ceil(std::nextafter(1., std::numeric_limits<double>::max()))
  • 下一个问题将是“这是如何在标准库中实现的”:P
  • 阅读@LightnessRacesinOrbit 的评论后,我很好奇。 This is how glibc implements nextafter, and this is how musl implements it 以防其他人想看看它是如何完成的。基本上:原始位玩弄。
  • @Cornstalks:我并不感到惊讶,因为它只是有点玩弄,唯一的其他选择就是支持 CPU。
  • Bit twiddling 是唯一正确的方法,IMO。你可以做很多测试尝试,慢慢接近它,但这可能会很慢。
【解决方案2】:

在 C 中,你可以这样使用:

#include <float.h>
...
double value = 1.0+DBL_EPSILON;

DBL_EPSILON 是 1 与可表示的大于 1 的最小值之间的差。

您需要将其打印成几位数字才能看到实际值。

在我的平台上,printf("%.16lf",1.0+DBL_EPSILON) 提供1.0000000000000002

【讨论】:

  • 因此对于1. 以外的其他值,1'000'000 Demo 将不起作用
  • @Jarod42:你说得对,但 OP 专门询问了 1.0。顺便说一句,它还给出了大于 1 的最接近的值,而不是最接近 1 的值(可能小于 1)。所以我同意这是一个部分答案,但我认为它仍然可以做出贡献。
  • @LưuVĩnhPhúc:我对答案的限制给出了精确度,并且在另一个方向上最接近。
  • 这并没有将 最接近 双倍的值设为 1.0,因为(假设基数为 2)双倍右 before 1.0 的距离仅为 1.0 的一半双右之后 1.0(这是你计算的那个)。
  • @celtschk:你说得对,我在上面的评论中已经解释过了。
【解决方案3】:

在 C++ 中你也可以使用这个

1 + std::numeric_limits<double>::epsilon()

【讨论】:

  • 就像 barak manos 的回答一样,这不适用于 1 以外的任何值。
  • @zwol 专门用于典型的二进制浮点实现,它适用于 1 到 2-epsilon 之间的任何值。但是,是的,你是对的,它只保证适用于 1。
  • 从技术上讲,它不适用于 1,因为最接近 1 的数字是 1 之前的数字,而不是 1 之后的数字。 double 在 0.5 和 1 之间的精度是其在 1 和 2 之间的精度的两倍,因此 1 之前的数字最终更接近 1。
【解决方案4】:

在 C 和 C++ 中,以下给出最接近 1.0 的值:

#include <limits.h>

double closest_to_1 = 1.0 - DBL_EPSILON/FLT_RADIX;

但请注意,在更高版本的 C++ 中,limits.h 已被弃用,取而代之的是 climits。但是,如果您仍然使用 C++ 特定代码,则可以使用

#include <limits>

typedef std::numeric_limits<double> lim_dbl;
double closest_to_1 = 1.0 - lim_dbl::epsilon()/lim_dbl::radix;

正如 Jarod42 在他的回答中所写,从 C99 或 C++11 开始,您还可以使用 nextafter

#include <math.h>

double closest_to_1 = nextafter(1.0, 0.0);

当然,在 C++ 中,您可以(并且对于更高版本的 C++ 版本应该)包含 cmath 并改用 std::nextafter

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-07
    • 1970-01-01
    • 2011-02-28
    • 1970-01-01
    • 1970-01-01
    • 2013-07-14
    • 1970-01-01
    相关资源
    最近更新 更多