C++ 可以在内部表示而无需舍入的 0 和 1 之间的最大和最小数字是多少?
what are the largest and smallest numbers between 0 and 1 that C++ can represent internally without rounding?
我有一个基于简单模型计算概率的 C++ 函数。似乎 C++ 倾向于将非常小的概率四舍五入为 0,将非常大的概率四舍五入为 1。这会导致以后的计算出现问题(采用 log(p) 和 log(1-p))。
有没有一种方法可以显式地表示 C++ 可以在内部表示的小于 1 的最大数而不进行舍入?同样,大于0的最小数?
我可以这样做:
if (probability == 1)
probability = 0.999999999;
else if (probability == 0)
probability = 0.000000001;
但这会导致其他数值问题(与累积数字有关)。有没有更原则的方法,也许使用 numeric_limits?
您可以通过使用 DBL_EPSILON
(或 FLT_EPSILON
根据您的数据类型)来使用 <cfloat>
header:
DBL_EPSILON
被定义为满足您要求的 1.0 + DBL_EPSILON != 1.0
的最小值。
#include <cfloat>
if (probability == 1.0)
probability = 1.0 - DBL_EPSILON;
else if (probability == 0.0f)
probability = DBL_EPSILON;
或者您可以使用 <limits>
header 并可能在您的浮点类型上对其进行参数化:
#include <limits>
if (probability == 1.0)
probability = 1.0 - std::numeric_limit<double>::epsilon();
else if (probability == 0.0f)
probability = std::numeric_limit<double>::epsilon();
实际上后者 returns 值由第一种方法定义,因此在实现上没有区别。
C++ double
变量为双精度浮点数。 IEEE 754 标准指定 binary64 具有:
- 符号位:1位
- 指数宽度:11位
- 有效位数精度:53 位(显式存储 52 位)
这给出了 15–17 位有效小数位的精度。如果将最多 15 位有效数字的十进制字符串转换为 IEEE 754 双精度表示,然后再转换回具有相同有效数字位数的字符串,则最终字符串应与原始字符串匹配。查看 this document 了解更多信息。
虽然它的名字可能有点误导,但 std::numeric_limits<T>::min
正是您要找的。对于浮点类型,它将为您提供大于零的最小值。
对于仍然小于1
的最大数,如果你使用C++11,你可以使用std::nexttoward
:
代码
#include<cstdio>
#include<cmath>
#include<limits>
int main(){
printf("Near 0: %1.20e\nNear 1: %1.20e\n",
std::numeric_limits<double>::min(),
std::nexttoward(1.0, 0.0)
);
return 0;
}
结果
Near 0: 2.22507385850720138309e-308
Near 1: 9.99999999999999888978e-01
我有一个基于简单模型计算概率的 C++ 函数。似乎 C++ 倾向于将非常小的概率四舍五入为 0,将非常大的概率四舍五入为 1。这会导致以后的计算出现问题(采用 log(p) 和 log(1-p))。
有没有一种方法可以显式地表示 C++ 可以在内部表示的小于 1 的最大数而不进行舍入?同样,大于0的最小数?
我可以这样做:
if (probability == 1)
probability = 0.999999999;
else if (probability == 0)
probability = 0.000000001;
但这会导致其他数值问题(与累积数字有关)。有没有更原则的方法,也许使用 numeric_limits?
您可以通过使用 DBL_EPSILON
(或 FLT_EPSILON
根据您的数据类型)来使用 <cfloat>
header:
DBL_EPSILON
被定义为满足您要求的 1.0 + DBL_EPSILON != 1.0
的最小值。
#include <cfloat>
if (probability == 1.0)
probability = 1.0 - DBL_EPSILON;
else if (probability == 0.0f)
probability = DBL_EPSILON;
或者您可以使用 <limits>
header 并可能在您的浮点类型上对其进行参数化:
#include <limits>
if (probability == 1.0)
probability = 1.0 - std::numeric_limit<double>::epsilon();
else if (probability == 0.0f)
probability = std::numeric_limit<double>::epsilon();
实际上后者 returns 值由第一种方法定义,因此在实现上没有区别。
C++ double
变量为双精度浮点数。 IEEE 754 标准指定 binary64 具有:
- 符号位:1位
- 指数宽度:11位
- 有效位数精度:53 位(显式存储 52 位)
这给出了 15–17 位有效小数位的精度。如果将最多 15 位有效数字的十进制字符串转换为 IEEE 754 双精度表示,然后再转换回具有相同有效数字位数的字符串,则最终字符串应与原始字符串匹配。查看 this document 了解更多信息。
虽然它的名字可能有点误导,但 std::numeric_limits<T>::min
正是您要找的。对于浮点类型,它将为您提供大于零的最小值。
对于仍然小于1
的最大数,如果你使用C++11,你可以使用std::nexttoward
:
代码
#include<cstdio>
#include<cmath>
#include<limits>
int main(){
printf("Near 0: %1.20e\nNear 1: %1.20e\n",
std::numeric_limits<double>::min(),
std::nexttoward(1.0, 0.0)
);
return 0;
}
结果
Near 0: 2.22507385850720138309e-308 Near 1: 9.99999999999999888978e-01