C/C++中的最小双精度值

112

在 C/C++ 程序中,有没有标准和/或可移植的方法来表示最小的负值(例如使用负无穷)?

float.h 中的 DBL_MIN 是最小的正数


4
我会选择-DBL_MAX,但我相信肯定有一些技术上的原因,这样做不太合适 :-) - anon
4
@Neil,没有,这不像二进制补码整数。 - fortran
我还没有在标准中发现任何关于浮点类型范围必须围绕零对称的规定。但是limits.h和<limits>中的常量表明,C和C++标准都希望它们是如此。 - Steve Jessop
@onebyone,浮点数的符号位在第31位,双精度浮点数的符号位在第63位。因此,取任何正值并将其符号位设置为1,那么你就得到了相同大小但是负数,这就是为什么它是对称的。如果你想知道,是的,有正零和负零。 - fortran
4
实际上,在float.h中的DBL_MIN是最小的正规化数。还有比它更小的数。 - fdermishin
1
@fortran: IEEE 754浮点数使用一个符号位,而且现在大多数浮点数硬件都是IEEE 754。但是C和C++支持非IEEE 754浮点数硬件,因此问题在于语言是否保证-DBL_MAX必须等于最小可表示值。 - j_random_hacker
10个回答

151

这似乎是最标准和可移植的。 - Will
这是我打负分的解释:谁或什么说C或C++语言保证了-DBL_MAX可表示,更不用说最小可表示值了?大多数FP硬件符合IEEE 754标准,并使用此表示,但这并不意味着-DBL_MAX在任何标准符合的C平台上都能正常工作。 - j_random_hacker
@j_random_hacker:请看下面Fortran的回答。 - JohnTortugo
3
这是一个非常好的观点,但是C标准要求-DBL_MAX必须能够被精确表示,因此如果FP硬件无法实现该要求,则实现只需绕过该要求即可。请参阅C99中* 5.2.4.2.2浮点类型的特征<float.h> p2 *中的浮点模型(自那时以来可能已移至其他位置)。 - user743382
3
是的,但p2规定e_min和e_max与符号位无关,因此 DBL_MAX 精确地等于 (1 − b^−p)b^e_max,它是可以精确表示的,最小有限值精确地等于 -(1 − b^−p)b^e_max,并且由于这恰好是 -DBL_MAX,因此取反 DBL_MAX 也不会引入任何舍入误差。 - user743382
显示剩余2条评论

77

浮点数(IEEE 754)是对称的,因此如果你可以表示最大值(DBL_MAXnumeric_limits<double>::max()),只需在其前面加上减号。

还有一种很酷的方式:

double f;
(*((uint64_t*)&f))= ~(1LL<<52);

9
谢谢指出浮点数的对称性,给你点赞👍。 - Andrew Hare
6
不使用 IEEE 754 浮点数的 C/C++ 实现怎么办? - Steve Jessop
2
gcc的手册中关于-ffast-math的说明如下: “设置-fno-math-errno,-funsafe-math-optimizations,-ffinite-math-only,-fno-rounding-math,-fno-signaling-nans和-fcx-limited-range。 由于它可能导致依赖于IEEE或ISO数学函数规则/规范的精确实现的程序输出不正确,因此此选项未被任何-O选项打开。但是,对于不需要这些规范保证的程序,它可能会产生更快的代码。” 快速数学是一种常见的设置,例如Intel ICC默认使用它。 总之,我不确定这对我意味着什么 :-) - Will
4
这意味着实现不使用IEEE 754算术,但公平地说,这些选项仍然使用IEEE表示法。您可能会发现一些仿真库使用非IEEE表示法,因为并非所有处理器都具有本机浮点格式(尽管它们可能发布一个包含格式的C ABI,对应于制造商提供的仿真库)。因此,并非所有编译器都可以使用一个。这取决于您在询问“标准和/或可移植性”时的意思,原则上可移植和实际上的可移植是不同的。 - Steve Jessop
3
你所说的对于IEEE 754标准是正确的,但该标准并不要求使用这种编码(正如@SteveJessop指出的那样,在实践中可移植性与原则上的可移植性并不相同)。 - Christophe
显示剩余7条评论

55

在C语言中,使用

#include <float.h>

const double lowest_double = -DBL_MAX;
在C++ 11之前,使用:

In C++pre-11, use


#include <limits>

const double lowest_double = -std::numeric_limits<double>::max();
在C++11及以后的版本中,请使用:
#include <limits>

constexpr double lowest_double = std::numeric_limits<double>::lowest();

在C++11之前,min()函数是不可用的吗?还是它与-max()表示的值不同? http://en.cppreference.com/w/cpp/types/numeric_limits - Alexis Wilke
7
@Alexis: 如果你查看你提供的页面上最低的三行表格,你会发现min可以得到绝对值最小的正数,而lowest可以得到绝对值最大的负数。是的,这很糟糕。欢迎来到 C++ 标准库的精彩世界 :-P - rubenvb
对于 C 语言,浮点数的定义在 float.h 中。而整数则需要使用 limits.h - Ciprian Tomoiagă

34

试试这个:

-1 * numeric_limits<double>::max()

参考:numeric_limits

此类为每种基本类型进行了特化, 其成员返回或设置不同的值,这些值定义了该类型在特定平台上编译时具有的属性。


1
为什么不直接使用-numeric_limits<double>::max() - k06a
4
在这么长的表达式中,使用单个字符表示否定,即使字符串中有“max”一词,也肯定会让某个人迟早出错。要么将其存储在描述性变量中,要么使用“-1 * ...”来使其更加清晰。 - Filip Haglund

22

您是在寻找实际无限还是最小有限值?如果是前者,请使用

-numeric_limits<double>::infinity()

只有在某种情况下才有效

numeric_limits<double>::has_infinity

否则,您应该使用

numeric_limits<double>::lowest()

这是在C++11中引入的新特性。

如果lowest()不可用,您可以改为使用

-numeric_limits<double>::max()

在原则上,lowest() 可能与其不同,但通常实际上不会有区别。


有限值和无限值之间的差异加1!但标准并不保证对称浮点编码。因此,即使在实践中它可以工作,但在理论上 -numeric_limits<double>::max() 并不完全可移植。 - Christophe
@Christophe: [x] 已修复 - Christoph

12

一种真正的可移植C++解决方案

从C++11开始,您可以使用numeric_limits<double>::lowest()。根据标准,它会返回您要查找的精确值:

一个有限的值x使得没有其他有限的值y满足y < x。对于所有is_bounded != false的特化都是有意义的。

在线演示


这里有很多不可移植的C ++解决方案!

有很多答案都采用了-std::numeric_limits<double>::max()

幸运的是,在大多数情况下,它们都能正常工作。浮点编码方案将数字分解为尾数和指数,并且大多数编码方案(例如流行的IEEE-754)使用不属于尾数的不同符号位。这使得通过翻转符号将最大的正数变成最小的负数。

enter image description here

为什么这些答案不可移植?

标准没有强制要求任何浮点标准。

我同意我的论点有些理论化,但假设某些古怪的编译器制造商使用一种革命性的编码方案,其中尾数使用某些变体的二进制补码进行编码。 二进制补码编码不是对称的。例如,对于带符号的8位char类型,最大正值为127,而最小负值为-128。因此,我们可以想象某些浮点编码显示类似的不对称行为。

我不知道是否存在这样的编码方案,但关键在于标准不能保证符号翻转产生所期望的结果。因此,这个流行的答案(对不起,伙计们!)不能被认为是完全可移植的标准解决方案! /* 至少在没有断言numeric_limits<double> ::is_iec559 为真的情况下 */


7
- std::numeric_limits<double>::max()

应该完全可以正常工作

数字极限


2
有没有标准和/或可移植的方法来表示C(++)程序中最小的负值(例如使用负无穷大)?
C方法。
许多实现支持+/-无穷大,因此最小的双精度浮点数是-INFINITY。
#include <math.h>
double most_negative = -INFINITY;

有没有一种标准和/或可移植的方法...?

现在我们还需要考虑其他情况:

  • 没有无穷大

只需使用-DBL_MAX

  • 仅有一个无符号无穷大。

在这种情况下,我认为OP会更喜欢-DBL_MAX

  • DBL_MAX的大小还要大的非规范化值。

这是一个不寻常的情况,可能超出了OP的关注范围。当double被编码为一对浮点数以实现所需的范围/精度时(参见double-double),存在一个最大的规范化double和一个更大的非规范化值。我已经看到有人争论DBL_MAX应该指的是最大的规范化,还是两者中最大的。

幸运的是,这种成对的方法通常包括一个负无穷大,因此最小值仍然是-INFINITY


为了更好的可移植性,代码可以走这条路...

// HUGE_VAL is designed to be infinity or DBL_MAX (when infinites are not implemented)
// .. yet is problematic with unsigned infinity.
double most_negative1 = -HUGE_VAL;  

// Fairly portable, unless system does not understand "INF"
double most_negative2 = strtod("-INF", (char **) NULL);

// Pragmatic
double most_negative3 = strtod("-1.0e999999999", (char **) NULL);

// Somewhat time-consuming
double most_negative4 = pow(-DBL_MAX, 0xFFFF /* odd value */);

// My suggestion
double most_negative5 = (-DBL_MAX)*DBL_MAX;

1
原始问题涉及无限大。那么,为什么不使用?
#define Infinity  ((double)(42 / 0.0))

根据IEEE的定义?当然可以否定这一点。

好主意!而且它有效。但仅当numeric_limits<double>::has_infinity && ! numeric_limits<double>::traps时。 - Christophe

-1
如果您没有启用浮点异常(在我看来,您不应该这样做),您可以简单地说:
double neg_inf = -1/0.0;

这将产生负无穷大。如果您需要一个浮点数,您可以将结果转换为浮点数。

float neg_inf = (float)-1/0.0;

或使用单精度算术

float neg_inf = -1.0f/0.0f;

结果总是相同的,单精度和双精度都只有一种负无穷大的表示方式,并且它们之间的转换也如你所期望的那样。


为什么不直接写-INFINITY,而要这样做呢? - M.M
此外,无限可能存在,如果它确实存在,则在标准C中可能无法区分正负。 - M.M
在许多编译器和/或架构中,如果您传播无穷大和NaN值,您的C/C++代码将会变得非常缓慢。 - markgalassi
@markgalassi 请仔细看:您会注意到neg_inf被初始化为常量值。编译器将负责计算inf值。当您将其用作计算最大值的空值时,第一次迭代通常会用更大的值覆盖它。也就是说,性能几乎不是问题。而且OP明确要求“例如使用负无穷大”,而-inf确实是唯一正确的答案。您已经对一个正确且有用的答案进行了负面评价。 - cmaster - reinstate monica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接