程序给出不准确的值

Program giving inaccurate value

提问人:Udesh 提问时间:4/20/2023 最后编辑:Udesh 更新时间:4/21/2023 访问量:56

问:

下面的程序计算 262 + 261 + 260 + ... + 21 + 20

使用类型存储:doublesum

double sum = 0;
  for (int i = 0; i < 63; i++) {
    sum += pow(2.0, i);
    // print("i : $i sum : $sum");
  }
  print(sum);

输出:

9223372036854776000.0

很明显,答案应该是一个数字,但 is : 这是一个数字。oddsum9223372036854776000.0even

当我将总和更改为 int 数据类型时,它给出了准确的结果:

使用类型存储:intsum

int sum = 0;
for (int i = 0; i < 63; i++) {
  sum += pow(2.0, i).toInt();
  //print("i : $i sum : $sum");
}
print(sum);

输出:

9223372036854775807

当数据类型为 double 时,为什么求和会产生错误的结果?

飞镖 浮动精度

评论


答:

3赞 Mark Adler 4/20/2023 #1

因为 a 的有效位只有 53 位有效位,而你的数字有 63 位。double

52 存储在主有效字段中,其中 53 位二进制分数的前导位为隐式。1

1 sign bit, 11 exponent bits, and 52 significand bits

评论

2赞 Mark Adler 4/20/2023
尾数有 52 位存储,带有隐含的前导位。因此,如果计算隐含位,则可以将其视为 53。1
1赞 Eric Postpischil 4/20/2023
IEEE 754 标准使用的术语是“significand”而不是“mantiissa”。浮点表示的分数部分是有效。“尾数”是对数分数部分的旧术语。有效数是线性的;添加到有效值并添加到所表示的数字中。尾数是对数的;将尾数相加会使所代表的数字成倍增加。
3赞 jamesdlin 4/21/2023
@Udesh 请注意,打印并不一定告诉您有关有效位数的任何信息。位在有效和控制精度,而不是大小。这两个表达式只需要一个有效位。根据用于将二进制浮点数转换为十进制字符串的算法,这两个表达式最终可能会打印相同的内容¹。更好的测试是检查。1 << 53pow(2.0, 53)var x = (1 << 53).toDouble(); print(x == x + 1);
1赞 jamesdlin 4/21/2023
¹ 例如,在我的系统上使用标准 C 库实现,打印相同的值(不包括尾随小数零)。Dart 碰巧打印不同的值,因为它的转换方式不同。int64_t x = 1; x <<= 62; printf("%"PRId64"\n", x); printf("%f\n", (double) x);doubleString
1赞 jamesdlin 4/21/2023
@Udesh 不,2^53 最多需要使用有效位的一位。它没有告诉你有效使用的位数。另一方面,2^53 - 1 将使用所有有效位。