最接近1.0的双精度数是不是1.0?


88

有没有办法以编程方式获取最接近1.0的double,但实际上不是1.0?

一种可行的方法是将双精度数存为相同大小的整数,然后减去一个。IEEE754浮点格式的工作方式是,将小数部分从全零(1.000000000000)更改为全1(1.111111111111),最终将指数减小了一个。但是,有些机器将整数存储在低位在前,而浮点存储在大在前,因此这并不总是可行。


4
您不能假设+1与-1是相同的距离(从1.0开始)。基数10和基数2浮点表示的交织意味着间隙不均匀。
理查德·克里顿

2
@理查德:你是对的。减去一个ULP不太可能获得er“ nextbefore”值,因为我想也必须调整指数。nextafter()是实现他想要的唯一正确方法。
Rudy Velthuis '16

1
仅供参考,请阅读以下博客(
非我的

1
@RudyVelthuis:它适用于每种IEEE754二进制浮点格式。
埃德加·博内特

1
好的,然后告诉我:“什么对每种IEEE754浮点格式都有效”?完全不正确的是,如果减小有效位数,则会得到“ firstbefore()”值,尤其是对于1.0而言,尤其如此,它的有效位数为2的幂。这意味着1.0000...二进制要递减0.111111....并标准化,您必须将其向左移动:1.11111...这要求您递减指数。然后您距1.0仅有2 ulp。因此,不,从积分值中减去1不会给您这里的要求。
Rudy Velthuis '16

Answers:


22

在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


143

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

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

演示版


11
这也是将double递增到下一个可表示整数的好方法std::ceil(std::nextafter(1., std::numeric_limits<double>::max()))
Johannes Schaub-litb

43
下一个问题将是“如何在stdlib中实现”:P
Lightness Races in Orbit

17
阅读@LightnessRacesinOrbit的评论后,我感到好奇。这就是glibc的实现方式nextafter这也是musl的实现方式,以防其他人想知道它是如何完成的。基本上是:原始位旋转。
Cornstalks

2
@Cornstalks:我并不奇怪它会有点混乱,唯一的其他选择就是拥有CPU支持。
Matthieu M.

5
海事组织,位纠缠是唯一正确的方法。您可以进行许多测试尝试,尝试缓慢地进行处理,但这可能会非常缓慢。
Rudy Velthuis '16

25

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

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

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

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

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


10
因此,对于一些其他的价值比不会工作1.作为1'000'000 演示
Jarod42

7
@ Jarod42:您是对的,但OP专门询问1.0。顺便说一句,它也给出最接近的值大于1,而不是绝对最接近的值1(可能小于1)。因此,我同意这是部分答案,但我认为它仍然可以有所作为。
barak manos

@LưuVĩnhPhúc:我给出了答案限制的精确度,并且在另一个方向上最接近。
Jarod42 '16

7
这不会使最接近的 double 接近 1.0,因为(假设以2为底)1.0 之前的double 只是1.0 以后的double (您计算出的那个)的一半。
celtschk 2016年

@celtschk:对,我已经在上面的评论中对此进行了解释。
barak manos

4

在C ++中,您也可以使用

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

1
就像巴拉克·马诺斯(barak manos)的回答一样,它不适用于除1以外的任何值
。– zwol

2
对于典型的二进制浮点实现,@ zwol通常适用于介于1和2ε之间的任何值。但是,是的,你说得对,它只能保证适用于1
Random832

7
从技术上讲,它不适用于1,因为最接近1的数字是1之前的数字,而不是1之后的数字。 0.5和1之间的精度是两倍高其1和2之间的精度,因此前1个端部的数量直到接近1
HelloGoodbye
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.