对于单精度,最小位数保证为 6。
即9999978e3
和9999979e3
都将"收敛"到9999978496
.因此,无论我将使用什么小数,6 位数字始终由单精度浮点数学保证(至少对于 IEEE 754
)。
我认为同样适用于双精度,但最小值应为 15。我找不到证明这一点的十进制数,就像上面使用单精度一样。
你能给我一个吗?或者你会如何检索它?
9007199254740992
和 9007199254740993
都是 16 位数字,当存储为双精度IEEE754时,两者都具有值9007199254740992
。
即9007199254740993
的第 16 位数字是一个笑话。
我选择这个例子的灵感是,9007199254740992
是 2 的 54 次方,紧随IEEE754双精度类型的有效位数之后,第一个十进制数字恰好是9
。因此,尽管只有 16 位数字,但上面的奇数都无法表示!
坚持IEEE754双精度,如果你想要一个 0 到 1 范围内的例子,那么从二元有理0.75
开始,添加一个 order 1e-16
的值。很快,你会偶然发现0.7500000000000005
和0.7500000000000006
,它们都是0.75000000000000055511151231257827021181583404541015625
我已经详细说明(感谢@Bathsheba提示)一种算法,从小数部分开始,按所需的数字(在我的例子中是第 16 位)递增,将找到(对于以下 10000 位小数)小数,这些小数将与相同的二进制双精度IEEE754表示相冲突。随意调整它:
#include <iostream>
int main() {
std::cout.precision(100);
long long int decimalPart = 7500000000000005;
double value, temp = 0.0;
// add 1e-16 increment
for(int i = 0; i < 10000; i++) {
value = decimalPart / 1e16;
// found
if(temp == value) {
std::cout << "decimal found: 0." << decimalPart << std::endl;
std::cout << "it collides with: 0." << decimalPart - 1 << std::endl;
std::cout << "both stored (binary) as " << value << std::endl << std::endl;
}
decimalPart += 1;
temp = value;
}
}
您能否给我一个 16 位(或更多)十进制数,该十进制数仅在第 15 位正确转换为双精度浮点圆?
这样的数字并不罕见,所以很容易尝试限制在感兴趣范围内的各种字符串。
在 16 位十进制文本值的广泛范围内,大约 10% 失败。所有失败都以 '4'
或更多的前导数字开始 - 这并不奇怪。
// Although a C++ post, below is some C code
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void form_text_number(char *buf, int significant_digits, int min_expo, int max_expo) {
unsigned i = 0;
buf[i++] = (char) (rand() % 9 + '1');
buf[i++] = '.';
for (int sd = 1; sd < significant_digits; sd++) {
buf[i++] = (char) (rand() % 10 + '0');
}
sprintf(buf + i, "e%+03d", rand() % (max_expo - min_expo + 1) + min_expo);
}
bool round_trip_text_double_text(const char *s, int significant_digits) {
double d = atof(s);
char buf[significant_digits + 10];
sprintf(buf, "%.*e", significant_digits - 1, d);
if (strcmp(s, buf)) {
printf("Round trip failed "%s" %.*e "%s"n", s, significant_digits - 1 + 3,d, buf);
return false;
}
return true;
}
测试代码
void test_sig(unsigned n, int significant_digits, int min_expo, int max_expo) {
printf("Sig digits %2d: ", significant_digits);
while (n-- > 0) {
char buf[100];
form_text_number(buf, significant_digits, min_expo, max_expo);
if (!round_trip_text_double_text(buf, significant_digits)) {
return;
}
}
printf("None Failedn");
}
int main(void) {
test_sig(10000, 16, -300, 300);
test_sig(10000, 16, -1, -1);
test_sig(1000000, 15, -300, 300);
test_sig(1000000, 15, -1, -1);
return 0;
}
输出
Sig digits 16: Round trip failed "8.995597974696435e+110" 8.995597974696434373e+110 "8.995597974696434e+110"
Sig digits 16: Round trip failed "6.654469376627144e-01" 6.654469376627144550e-01 "6.654469376627145e-01"
Sig digits 15: None Failed
Sig digits 15: None Failed
备注:当许多失败字符串的double
打印为3个额外的数字时,这3位数字在445到555的范围内。
根据 IEEE 754,有效位数(或尾数)有 52 个显式位和一个隐式额外位。因此,所有 53 位的整数都精确地表示为 double
。54 位或更多位的整数将丢失低位,因此如果该位不为零,它们将无法精确表示。因此,未精确表示为double
的最小整数是1ULL << 53 + 1
显示它的程序:
#include <iostream>
#include <cstdint>
int main(int, char**) {
std::uint64_t i = (1ULL << 53) + 1;
double x = i;
std::uint64_t j = x;
std::cout << x << " " << i << " " << j << std::endl;
return 0;
}