最接近 1.0 的双倍是多少,不是 1.0?

What's the closest double to 1.0, that isn't 1.0?

提问人:jorgbrown 提问时间:8/6/2016 最后编辑:Jarod42jorgbrown 更新时间:8/7/2016 访问量:6848

问:

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

一种笨拙的方法是将双精度值转换为相同大小的整数,然后减去一。浮点格式的工作方式IEEE754,这最终会将指数减少 1,同时将小数部分从所有零 (1.000000000000) 更改为所有 1 (1.1111111111111)。但是,有些机器的整数存储在小端序,而浮点数存储在大端序,因此这并不总是有效。

C++ 浮点 精度

评论

4赞 Richard Critten 8/6/2016
您不能假设 +1 与 -1 的距离(从 1.0 开始)相同。以 10 为底数和以 2 为底数的浮点表示的交错意味着间隙是不均匀的。
2赞 Rudy Velthuis 8/6/2016
@Richard:你说得对。减去一个 ULP 不太可能得到“nextbefore”值,因为我想指数也必须调整。 是实现他想要的东西的唯一正确方法。nextafter()
1赞 Richard Critten 8/6/2016
仅供参考,请阅读此博客(不是我的):exploringbinary.com/...
1赞 Edgar Bonet 8/8/2016
@RudyVelthuis:它确实适用于所有IEEE754二进制浮点格式。
2赞 Rudy Velthuis 8/8/2016
好的,然后告诉我:“什么”适用于每种IEEE754浮点格式“?如果你递减有效数,你就会得到 “firstbefore()” 值,尤其是对于 1.0,它有一个 2 的幂,这是不正确的。这意味着二进制是递减的,要对其进行归一化,您必须将其向左移动:这需要您递减指数。然后你离 1.0 还有 2 ulp 的距离。所以不,从整数值中减去 1 并不能给你这里要求的内容。1.0000...0.111111....1.11111...

答:

157赞 Jarod42 8/6/2016 #1

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

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

演示

评论

14赞 Johannes Schaub - litb 8/6/2016
这也是将双精度递增到下一个可表示整数的好方法:.std::ceil(std::nextafter(1., std::numeric_limits<double>::max()))
45赞 Lightness Races in Orbit 8/6/2016
下一个问题是“如何在 stdlib 中实现”:P
19赞 Cornstalks 8/7/2016
看完@LightnessRacesinOrbit的评论后,我很好奇。这就是 glibc 在 nextafter实现的方式,也是 musl 实现它的方式,以防其他人想看看它是如何完成的。基本上:原始位摆弄。
3赞 Matthieu M. 8/8/2016
@Cornstalks:我并不感到惊讶,这归结为一点点的摆弄,唯一的其他选择是拥有 CPU 支持。
6赞 Rudy Velthuis 8/8/2016
IMO,Bit Twiddling 是正确执行此操作的唯一方法。你可以做很多测试尝试,试图慢慢接近它,但这可能非常慢。
23赞 barak manos 8/6/2016 #2

在 C 语言中,您可以使用以下命令:

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

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

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

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

评论

10赞 Jarod42 8/6/2016
因此,除了作为演示之外,这不适用于其他一些值1.1'000'000
9赞 barak manos 8/6/2016
@Jarod42:你说得对,但OP特别问了.顺便说一句,它还给出了大于 1 的最接近值,而不是绝对最接近 1 的值(可能小于 1)。所以我同意这是一个部分答案,但我认为它仍然可以做出贡献。1.0
0赞 Jarod42 8/6/2016
@LưuVĩnhPhúc:我给出了答案的限制的精确度,以及另一个方向的最接近性。
8赞 celtschk 8/6/2016
这并不能给出最接近 1.0 的双倍,因为(假设以 2 为基数)1.0 之前的双倍距离仅为 1.0 之后的双倍(即您计算的双倍)的一半。
0赞 barak manos 8/6/2016
@celtschk:你是对的,我在上面的评论中已经解释过了。
4赞 phuclv 8/6/2016 #3

在 C++ 中,你也可以使用它

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

评论

1赞 zwol 8/7/2016
就像巴拉克·马诺斯的回答一样,这不适用于 1 以外的任何值。
2赞 Random832 8/7/2016
@zwol,对于典型的二进制浮点实现,它适用于 1 到 2-epsilon 之间的任何值。但是,是的,你是对的,它只保证适用于 1。
9赞 HelloGoodbye 8/7/2016
从技术上讲,它不适用于 1,因为最接近 1 的数字是 1 之前的数字,而不是紧随其后的数字。double 在 0.5 和 1 之间的精度是其在 1 和 2 之间的精度的两倍,因此 1 之前的数字最终更接近 1。
26赞 celtschk 8/7/2016 #4

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

#include <limits.h>

double closest_to_1 = 1.0 - DBL_EPSILON/FLT_RADIX;

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

#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++ 版本应该)包含并使用。cmathstd::nextafter