Wikipedia about "guard bits"提供了一个代码示例:

#include <stdio.h>
int main(){
   double a;
   int i;

   a = 0.2; 
   a += 0.1; 
   a -= 0.3;

   for (i = 0; a < 1.0; i++) 
       a += a;

   printf("i=%d, a=%f\n", i, a);

   return 0;
}

使用我的zen2R7 4800hCPU,我用gcc Guard_digit.c -std=c17 -march=znver2 -pedantic -O0 -o With_Guard_digit.o编译了上面的源代码.然后,它的输出结果与维基百科i=54, a=1.000000相同.

正如这个note所说,IEEE标准已经实现了保护数字:

IEEE标准要求使用3个额外的较低有效位 比单精度中隐含的24位(尾数) 代表权.

尾数格式加上额外的位:

1.XXXXXXXXXXXXXXXXXXXXXXX   0   0   0                                                                                                                                          

^         ^                 ^   ^   ^
|         |                 |   |   |
|         |                 |   |   -  sticky bit (s)
|         |                 |   -  round bit (r)
|         |                 -  guard bit (g)
|         -  23 bit mantissa from a representation
-  hidden bit

问:有没有一种方法可以通过更改源代码或其他代码来解决这个精度和舍入问题(即误差偏移量可以达到一定程度,以便输出类似i=108, a=1.000000的结果)?

查看Eric Postpischil的答案后编辑:

很抱歉没有清楚地描述这个问题.我想知道如何通过保留原始计算来解决舍入问题,这样直接a = 0;就不考虑在内了.

我想解决这个具体的问题,但不是一般的问题.正如comment说的那样,这超出了我目前的范围.

推荐答案

问:有没有一种方法可以通过更改源代码或其他代码来解决这个精度和舍入问题(即误差偏移量可以达到一定程度,以便输出类似i=108, a=1.000000的结果)?

在常见的C实现中,不可能通过增加和/或减go 等于或大于.0625的值来产生a,这将导致所示的循环在迭代超过57次后终止.

这是因为常见的C实现对double使用IEEE-754二进制64位,也称为"双精度",而二进制64位使用53个有效位.这意味着以.0625开始的binade中的值由一个有效数表示,该有效数的高位具有位置值2()(0625),低位具有位置值2−56(跨越53位,包括两个端点).

正如小学算术中教授的那样,加法和减法可以将位进位到高位,但永远不能在最低输入位置以下生成非零位.因此,通过加减大于或等于.0625的值产生的任何结果都不能有任何小于2−56的非零位.

因此,在执行这样的运算后进入循环时,我们会遇到以下情况之一:

  • a为负或零,循环永远不会结束.
  • a等于2−56或更大,迭代57次或更少会使其大于1.

有没有办法通过更改源代码…来解决这个精度和舍入问题

显然,通过将源代码从以下位置更改可以获得0.2+0.1-0.3的正确结果:

a = 0.2; 
a += 0.1; 
a -= 0.3;

致:

a = 0;

这是计算中的一个常见问题:你不能通过问"我如何得到these个值的解决方案?"来正式描述你想要解决的一般问题,因为有一个简单的解决方案,它只是这些值的一个答案,它通常对你没有帮助.

相反,您必须描述整个类别的问题.例如,您可以问:"我如何编写代码来查找最多30个正数和负数的小数和,小数点后至多三位,小数点前两位?"

进一步注意,您不希望在另一个方向上走得太远,使问题完全是一般性的,而不是完全具体的.如果问题是没有错误地加减任何十进制数,那么您必须编写任意精度的算术.如果问题是用一些适度的位数加减一些适度数量的小数,那么这个问题可能可以通过使用经过精心 Select 的舍入的double个算术来解决.具体的解决方案可能取决于您 Select 的参数.因此,您需要很好地描述这个问题.

C++相关问答推荐

如何将一个enum类型类型转换为另一个类型?

为什么getchar()挂起了,尽管poll()返回了一个好的值?""

MISRA C:2012 11.3违规强制转换(FLOAT*)到(uint32_t*)

为什么在此程序中必须使用Volatile关键字?

在C++中头文件中声明外部 struct

是什么让numpy.sum比优化的(自动矢量化的)C循环更快?

用C++实现余弦函数

For循环中的变量行为不符合预期.[C17]

在C++中父进程和子进程中的TAILQ队列同步问题

GetText不适用于包含国际字符的帐户名称

S,在 struct 中创建匿名静态缓冲区的最佳方式是什么?

存储和访问指向 struct 的指针数组

在C中使用字符串时是否不需要内存分配?

System V 消息队列由于某种原因定期重置

创建 makefile 来编译位于不同目录中的多个源文件

在 C/C++ 中原子按位与字节的最佳方法?

比 * 更快的乘法

获取 struct 中匿名 struct 的大小

运行以下 C 程序时出现分段错误

定义 int a = 0, b = a++, c = a++;在 C 中定义了行为吗?