C语言中的浮点数采用IEEE 754标准来表示实数,但是由于计算机内部的存储方式和运算方式的限制,导致浮点数无法精确存储。
具体来说,计算机中使用二进制来表示浮点数,而二进制无法准确地表示某些小数部分,例如0.1这个小数。因此,在计算机中,浮点数是以近似值的形式存储的。
此外,计算机中使用的浮点数还受到指数位的影响。指数位决定了浮点数的小数部分的精度,但是指数位只能表示有限个二进制数字(通常为8位),因此也限制了浮点数的精度。
综上所述,由于计算机内部的存储方式和运算方式的限制,C语言中的浮点数无法精确存储。
C语言中的浮点数采用IEEE 754标准来表示实数,但是由于计算机内部的存储方式和运算方式的限制,导致浮点数无法精确存储。
具体来说,计算机中使用二进制来表示浮点数,而二进制无法准确地表示某些小数部分,例如0.1这个小数。因此,在计算机中,浮点数是以近似值的形式存储的。
此外,计算机中使用的浮点数还受到指数位的影响。指数位决定了浮点数的小数部分的精度,但是指数位只能表示有限个二进制数字(通常为8位),因此也限制了浮点数的精度。
综上所述,由于计算机内部的存储方式和运算方式的限制,C语言中的浮点数无法精确存储。
可以看一下 hadao 博客的文章“实型数据解析”
函数返回并没有看上去那么简单:return z;
语句之后,其实都是函数返回需要进行的操作。
首先是,return z;
返回值操作是,mov eax,dword ptr [ebp-8]
将 ebp-8(z)
处的两字(4字节)
的值存入 寄存器 eax
返回 z
的操作,其实就是将 z
的值,存入了寄存器中。
当函数使用完后,局部变量会被销毁,但是寄存器在CPU中是不会被销毁的。
所以将 局部变量 z
的值 存放在寄存器中,就能达到返回 z
的值 的效果。
再按顺序将:
在进入 Add
函数后 压栈的 edi
, esi
, ebx
三个内容退栈
然后将 ebp
的值 给 esp
当 esp
的值变为 ebp
的值的时候,Add
函数栈帧的维护就结束了。Add
函数栈帧的空间就会还给内存。
再然后就是 pop ebp
pop ebp
与 一般 pop
其他内容不同,pop ebp
还会将这里需要弹出的 ebp
的值 给 寄存器 ebp
此时,esp
和 ebp
两个维护栈帧的寄存器,就又去维护 main
函数栈帧了。
这整个过程的动画:
ret
指令Add
函数使用结束之后,汇编代码应该继续回到 main
函数中的 call
指令的下一条语句的地方:esp
和 ebp
重新维护main
函数栈帧的时候,esp
指向的地址,其实就是之前 call
指令执行时,压栈压入的 call
指令的下一条指令的地址:ret
指令执行之后,会直接把这个空间弹出栈,然后返回到这个空间存放的地址的指令处:这些步骤执行之后,逻辑成功从 Add
函数中返回到 main
函数中。
继续执行代码:add esp,8
:
mov dword ptr [ebp-20h],eax
:
代码走到这里,函数栈帧的大部分内容都已经讲的很清楚了。本篇文章到这里也就结束了。
该问题可能由于精度有效数字不够导致,出现了舍入误差。可以考虑使用高精度计算来避免产生舍入误差。具体实现方式可以参考以下几种语言的实现方案:
Python:
可以使用Python内置的decimal模块来实现高精度计算。在读入数据时,将输入的字符串转成decimal.Decimal类型,输出时再转成float类型即可。
示例代码:
from decimal import Decimal
total = Decimal(0)
while True:
try:
x = input()
total += Decimal(x)
if not x:
print(float(total))
total = Decimal(0)
except:
break
Java:
Java可以使用BigDecimal类实现高精度计算。在读入数据时,使用BigDecimal的构造函数将输入的字符串转成BigDecimal类型,输出时再使用BigDecimal的doubleValue()方法转成double类型即可。
示例代码:
import java.util.Scanner;
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
BigDecimal total = BigDecimal.ZERO;
Scanner scanner = new Scanner(System.in);
while (true) {
try {
String x = scanner.nextLine().trim();
total = total.add(new BigDecimal(x));
if (x.equals("")) {
System.out.println(total.doubleValue());
total = BigDecimal.ZERO;
}
} catch (Exception e) {
break;
}
}
scanner.close();
}
}
C++:
C++可以使用第三方库如GMP实现高精度计算。在读入数据时,使用GMP提供的函数将输入的字符串转成mpz_class类型,输出时再使用mpz_class的get_d()方法转成double类型即可。
示例代码:
#include <gmpxx.h>
#include <iostream>
#include <string>
using namespace std;
int main() {
mpz_class total(0);
string line;
while (true) {
try {
getline(cin, line);
if (line.empty()) {
cout << total.get_d() << endl;
total = mpz_class(0);
} else {
total += mpz_class(line);
}
} catch(...) {
break;
}
}
return 0;
}
注意,在使用GMP时需要先安装该库。
需要注意的是,使用高精度计算会带来一定的时间复杂度和空间复杂度,具体取决于所选用的高精度计算方法和数据规模。如需进一步优化程序,可以考虑使用一些高效的高精度计算算法,并适当控制数据规模。