为何sqrt(7)赋值的浮点数相乘无精度损失?C语言技术咨询
这是个特别棒的问题,刚好摸到了浮点数精度和标准库函数细节的门道,咱们一步步拆解清楚:
核心真相:sqrt(7)的平方其实也有误差,只是被printf藏起来了
你看到的a * a = 7.000000并不是真的完全等于7,只是这个误差小到printf默认的6位小数输出格式无法显示。
double类型的浮点数有大约15-17位有效数字,sqrt(7)返回的是当前double能表示的、最接近真实√7的数值。这个数值的平方其实是一个非常非常接近7的数——比如实际计算出来可能是7.000000000000002,但当用%lf(默认保留6位小数)输出时,这个微小的误差被四舍五入掉了,就显示成了7.000000。
为什么直接赋值2.645751会出现明显的精度损失?
真实的√7大约是2.6457513110645906...,你直接赋值的2.645751只取了前6位小数,这个十进制数本身无法被精确转换成二进制浮点数(绝大多数十进制小数都不行)。
当你写a = 2.645751时,编译器会把它转换成最接近的double值,这个值其实是2.645750999999999...,它的平方和7的差距刚好大到在6位小数下能被看出来,于是就显示成了6.999998。
用更高精度的输出验证一下
你可以修改代码,把printf的输出精度调高到15位小数,就能看到真实的差异:
#include <stdio.h> #include <math.h> int main () { double a; int i; i = 7; a = sqrt(i); printf("i = %d, a = %.15lf\n", i, a); printf("a * a = %.15lf\n", a*a); a = 2.645751; printf("a = %.15lf\n", a); printf("a * a = %.15lf\n", a*a); return(0); }
运行后会得到类似这样的结果:
i = 7, a = 2.645751311064591
a * a = 7.000000000000002
a = 2.645750999999999
a * a = 6.999998223001000
这下就能清楚看到:sqrt(7)的平方确实有微小误差,只是默认输出没显示;而直接赋值的数因为本身就是截断后的近似值,误差被放大了。
额外补充:标准库sqrt函数的优化
标准库的sqrt函数是经过专门优化的,它会返回在浮点数范围内最优的近似值,尽可能让这个值的平方接近原数。对于完全平方数(比如sqrt(4)),它会返回精确的2.0,平方后也完全等于4;但对于非完全平方数,还是会有微小的精度误差,只是通常被输出格式掩盖了。
内容的提问来源于stack exchange,提问作者Sleeping On a Giant's Shoulder

