浮点数精度问题(60分)

wzuxian2012 2013-02-13 10:04:36
非常老的一个话题, 以为搞懂了,没想到在实战中



case WM_PAINT:
{
HDC hdc = BeginPaint(hWnd, &ps);
RECT rc;
GetClientRect(hWnd,&rc);
SetViewportOrgEx(hdc,rc.right/2,rc.bottom/2,NULL);

float x=-360.0f;
float y=0.0f;

MoveToEx(hdc,x,0,NULL);
while(x<360.0f)
{
y=sin(x/57.3)*100;
LineTo(hdc,x,y);
MoveToEx(hdc,x,y,NULL);
x+=1.5f;
}
for(x=-360.0f;x<=360.0f;x+=90.0f)
{
TCHAR buf[20]={0};
y=sin(x/57.3)*100;
if(abs(y-0.0f)<0.00001f)
{
_stprintf(buf,_T("x=%d度;y=%f"),(int)x,y);
TextOut(hdc,x+4.0f,y+8.0f,buf,lstrlen(buf));
}
else if(abs(y-100.0f)<0.00001f)
{
_stprintf(buf,_T("x=%d度;y=%f"),(int)x,y);
TextOut(hdc,x+4.0f,y-8.0f,buf,lstrlen(buf));
}
}

EndPaint(hWnd, &ps);
return 0;
}
break;

需求:绘制一条正弦曲线,绘制后,在一些点的附近标注x,y的值。
(这些点为:y=0,y=+-100的点)

问题: 曲线绘正确了,但是 标注x,y的值不对,


截图吧:



很明显,少了一些标注了,比如y=-100.0f的地方就没有标注,怎么回事呢?



...全文
346 25 打赏 收藏 转发到动态 举报
写回复
用AI写文章
25 条回复
切换为时间正序
请发表友善的回复…
发表回复
c090869 2013-02-19
  • 打赏
  • 举报
回复
只能够回答一个小数能不能精确表示,其它就不大懂。
wzuxian2012 2013-02-19
  • 打赏
  • 举报
回复
引用 23 楼 c090869 的回复:
一个小数,如果在不断乘2过程中能得到一个整数,
那15楼呢?
c090869 2013-02-19
  • 打赏
  • 举报
回复
一个小数,如果在不断乘2过程中能得到一个整数,
赵4老师 2013-02-19
  • 打赏
  • 举报
回复
搜“浮点表示法”
赵4老师 2013-02-19
  • 打赏
  • 举报
回复
参考float.h
...
#define DBL_DIG         15                      /* # of decimal digits of precision */
#define DBL_EPSILON     2.2204460492503131e-016 /* smallest such that 1.0+DBL_EPSILON != 1.0 */
#define DBL_MANT_DIG    53                      /* # of bits in mantissa */
#define DBL_MAX         1.7976931348623158e+308 /* max value */
#define DBL_MAX_10_EXP  308                     /* max decimal exponent */
#define DBL_MAX_EXP     1024                    /* max binary exponent */
#define DBL_MIN         2.2250738585072014e-308 /* min positive value */
#define DBL_MIN_10_EXP  (-307)                  /* min decimal exponent */
#define DBL_MIN_EXP     (-1021)                 /* min binary exponent */
#define _DBL_RADIX      2                       /* exponent radix */
#define _DBL_ROUNDS     1                       /* addition rounding: near */

#define FLT_DIG         6                       /* # of decimal digits of precision */
#define FLT_EPSILON     1.192092896e-07F        /* smallest such that 1.0+FLT_EPSILON != 1.0 */
#define FLT_GUARD       0
#define FLT_MANT_DIG    24                      /* # of bits in mantissa */
#define FLT_MAX         3.402823466e+38F        /* max value */
#define FLT_MAX_10_EXP  38                      /* max decimal exponent */
#define FLT_MAX_EXP     128                     /* max binary exponent */
#define FLT_MIN         1.175494351e-38F        /* min positive value */
#define FLT_MIN_10_EXP  (-37)                   /* min decimal exponent */
#define FLT_MIN_EXP     (-125)                  /* min binary exponent */
#define FLT_NORMALIZE   0
#define FLT_RADIX       2                       /* exponent radix */
#define FLT_ROUNDS      1                       /* addition rounding: near */
...
wzuxian2012 2013-02-18
  • 打赏
  • 举报
回复
引用 14 楼 xiaopo_poxiao 的回复:
所有整数都是可以表示的 并不是所有小数都可以表示.具体情况具体分析!
你存储1.5,则1.1(2进制)就可以表示 1.1 是啥意思? 与1.1啥关系呢????
wzuxian2012 2013-02-18
  • 打赏
  • 举报
回复
引用 13 楼 xiaopo_poxiao 的回复:
引用 11 楼 wzuxian2012 的回复:引用 10 楼 xiaopo_poxiao 的回复:引用 7 楼 wzuxian2012 的回复:引用 6 楼 dubiousway 的回复:1. double 和 float ,天差地别。 ------- float x=0; //这里用double ,结果可是不一样多了。 int n=1000; while(n-……
懂了, 看一个小数是否可以被精确表示,有法子测试,就是: 用cout<<setprecision(10)来表示
寒沙胜雪 2013-02-18
  • 打赏
  • 举报
回复
所有整数都是可以表示的 并不是所有小数都可以表示.具体情况具体分析!
寒沙胜雪 2013-02-18
  • 打赏
  • 举报
回复
引用 11 楼 wzuxian2012 的回复:
引用 10 楼 xiaopo_poxiao 的回复:引用 7 楼 wzuxian2012 的回复:引用 6 楼 dubiousway 的回复:1. double 和 float ,天差地别。 ------- float x=0; //这里用double ,结果可是不一样多了。 int n=1000; while(n--) x+=0.1; printf("%f\……
你写个test程序,输出的时侯多输出几位小数

#include <iostream>
using namespace std;

int main()
{
float a=0.0;
for(int i=0; i<100; i++)
a += 0.1f;
cout <<a;
return 0;
}


#include <iostream>
#include <iomanip>
using namespace std;

int main()
{
float a=0.0;
for(int i=0; i<100; i++)
a += 0.1f;
cout << setprecision(10) << a << endl;
return 0;
}

你再把上面的0.1f改成1.5f试试
寒沙胜雪 2013-02-18
  • 打赏
  • 举报
回复
引用 11 楼 wzuxian2012 的回复:
引用 10 楼 xiaopo_poxiao 的回复:引用 7 楼 wzuxian2012 的回复:引用 6 楼 dubiousway 的回复:1. double 和 float ,天差地别。 ------- float x=0; //这里用double ,结果可是不一样多了。 int n=1000; while(n--) x+=0.1; printf("%f\……
0.1无法精确表示,无论是double 还是 float!1.5就可以.
wzuxian2012 2013-02-18
  • 打赏
  • 举报
回复
引用 10 楼 xiaopo_poxiao 的回复:
引用 7 楼 wzuxian2012 的回复:引用 6 楼 dubiousway 的回复:1. double 和 float ,天差地别。 ------- float x=0; //这里用double ,结果可是不一样多了。 int n=1000; while(n--) x+=0.1; printf("%f\n",x); ---------- 2。 ……
你本身存储0.1的时候,无法表示0.1,只能无限接近,但若你存储1.5,则1.1(2进制)就可以表示 啥意思??
wzuxian2012 2013-02-18
  • 打赏
  • 举报
回复
还有人吗???
wzuxian2012 2013-02-18
  • 打赏
  • 举报
回复
引用 18 楼 zhao4zhong1 的回复:
用10进制小数不能精确表示某些三进制小数0.1(3)=0.33333333333……(10) 同理,用二进制小数也不能精确表示某些10进制小数。 C/C++ code?123456789101112131415161718192021222324//Round(1.234,2) = 1.23//Round(1.234,0) = 1.0//Round(123.4,-1)……
如何知道一个小数是否能够被精确表示或者不精确的表示呢? 谁知道?
赵4老师 2013-02-18
  • 打赏
  • 举报
回复
用10进制小数不能精确表示某些三进制小数0.1(3)=0.33333333333……(10) 同理,用二进制小数也不能精确表示某些10进制小数。
//Round(1.234,2) = 1.23
//Round(1.234,0) = 1.0
//Round(123.4,-1) = 120.0
double Round(double dVal, short iPlaces) {
    double dRetval;
    double dMod = 0.0000001;
    if (dVal<0.0) dMod=-0.0000001;
    dRetval=dVal;
    dRetval+=(5.0/pow(10.0,iPlaces+1.0));
    dRetval*=pow(10.0,iPlaces);
    dRetval=floor(dRetval+dMod);
    dRetval/=pow(10.0,iPlaces);
    return(dRetval);
}

double round(double dVal, short iPlaces) //iPlaces>=0
{
    unsigned char s[20];
    double dRetval;

    sprintf(s,"%.*lf",iPlaces,dVal);
    sscanf(s,"%lf",&dRetval);
    return (dRetval);
}
wzuxian2012 2013-02-18
  • 打赏
  • 举报
回复
15,16楼,还有人指点一二吗
寒沙胜雪 2013-02-15
  • 打赏
  • 举报
回复
原来楼主是说缺少点.....
寒沙胜雪 2013-02-15
  • 打赏
  • 举报
回复
浮点数没研究..我的在vc6.0上可以啊.
	
case WM_PAINT:
{
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd,&rc);
SetMapMode(hdc, MM_LOENGLISH);
SetViewportOrgEx(hdc,rc.right/2,rc.bottom/2,NULL);

//draw y=100sin(x) sine line
MoveToEx(hdc,(int)(x),0,NULL);
while(x<360.0f)
{
y=(float)sin(x/57.3)*100;
LineTo(hdc,(int)(x),(int)y);
MoveToEx(hdc,(int)(x),(int)y,NULL);
x+=1.5f;
}
for(x=-360.0f;x<=360.0f;x+=90.0f)
{
TCHAR buf[40] = {0};
y=(float)sin(x/57.3)*100;
if(fabs(y-0.0f)<0.00001f)
{
_stprintf(buf,_T("x=%d度;y=%f"),(int)x,y);
TextOut(hdc,x+4.0f,y+8.0f,buf,lstrlen(buf));
}
else if( fabs(y-100.0f)<0.00001f )
{
_stprintf(buf,_T("x=%d度;y=%f"),(int)x,y);
TextOut(hdc,x+4.0f,y+8.0f,buf,lstrlen(buf));
}
else if(fabs(y+100.0f)<0.00001f)
{
_stprintf(buf,_T("x=%d度;y=%f"),(int)x,y);
TextOut(hdc,x+4.0f,y-8.0f,buf,lstrlen(buf));
}
}

EndPaint(hwnd, &ps);
return 0;
}

return 0;

寒沙胜雪 2013-02-15
  • 打赏
  • 举报
回复
引用 7 楼 wzuxian2012 的回复:
引用 6 楼 dubiousway 的回复:1. double 和 float ,天差地别。
-------
float x=0; //这里用double ,结果可是不一样多了。
int n=1000;
while(n--)
x+=0.1;
printf("%f\n",x);
----------

2。
1.5和0.5, 自然是不一样的精度。我也只是……


所有整数都是可以表示的 并不是所有小数都可以表示.
他不可能准确的存储0.1 只能接近0.1
2的-1 == 0.5 2的-2 == 0.25 2的-3 == 0.125.
你本身存储0.1的时候,无法表示0.1,只能无限接近,但若你存储1.5,则1.1(2进制)就可以表示,不会失真.

对于你的工程,PI本身就是无限循环小数,只能无限接近,然后你除去,所以得到的结果和真实的正弦值不一样.
对于没有点的输出,你可以把绝对值小于的数扩大点,0.5f,就可以输出所有的点了.


#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <math.h>


TCHAR szAppName[] = TEXT("Test");

LRESULT CALLBACK TestWinProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);

int WINAPI WinMain(
HINSTANCE hInstance, // handle to current instance
HINSTANCE hPrevInstance, // handle to previous instance
LPSTR lpCmdLine, // command line
int nCmdShow // show state
)
{
WNDCLASS wndcls;
HWND hwnd;
BOOL bRet;
MSG msg;



hInstance = hInstance;


wndcls.cbClsExtra = 0;
wndcls.cbWndExtra = 0;
wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndcls.hCursor = LoadCursor(NULL, IDC_ARROW);
wndcls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndcls.hInstance = hInstance;
wndcls.lpfnWndProc = TestWinProc;
wndcls.lpszClassName = szAppName;
wndcls.lpszMenuName = NULL;//无菜单
wndcls.style = CS_VREDRAW | CS_HREDRAW;

if (!RegisterClass(&wndcls))
{
MessageBox(NULL, TEXT("此程序需要在WINDOWS NT操作系统下运行!"),
szAppName, MB_ICONERROR);
return 0;
}

hwnd = CreateWindow(szAppName, TEXT("Test"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (-1 == bRet)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}


return 0;
}

LRESULT CALLBACK TestWinProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
static int cxChar;


HDC hdc;
PAINTSTRUCT ps;
TEXTMETRIC tm;
RECT rc;

float x=-360.0f; float y=0.0f;// for draw sine line


switch(uMsg)
{
case WM_CREATE:
hdc = GetDC(hwnd);
SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

GetTextMetrics(hdc, &tm);
cxChar = tm.tmAveCharWidth;
return 0;

case WM_PAINT:
{
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd,&rc);
SetMapMode(hdc, MM_LOENGLISH);
SetViewportOrgEx(hdc,rc.right/2,rc.bottom/2,NULL);

//draw y=100sin(x) sine line
MoveToEx(hdc,(int)(x),0,NULL);
while(x<360.0f)
{
y=(float)sin(x/57.3)*100;
LineTo(hdc,(int)(x),(int)y);
x+=1.5f;
}
for(x=-360.0f;x<=360.0f;x+=90.0f)
{
TCHAR buf[40] = {0};
y=(float)sin(x/57.3)*100;
if(fabs(y-0.0f)<0.05f)
{
_stprintf(buf,_T("x=%d度;y=%f"),(int)x,y);
TextOut(hdc,x,y,buf,lstrlen(buf));
}
else if(fabs(y-100.0f)<0.05f)
{
_stprintf(buf,_T("x=%d度;y=%f"),(int)x,y);
TextOut(hdc,x,y,buf,lstrlen(buf));
}
else if(fabs(y+100.0f)<0.05f)
{
_stprintf(buf,_T("x=%d度;y=%f"),(int)x,y);
TextOut(hdc,x,y,buf,lstrlen(buf));
}
}

EndPaint(hwnd, &ps);
return 0;
}

return 0;

case WM_CLOSE:
DestroyWindow(hwnd);
return 0;

case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);

}


dubiousway 2013-02-14
  • 打赏
  • 举报
回复
当 y== -100.0f的时候, 下面两条判断语句,走哪一条啊?呵呵。 if(abs(y-0.0f)<0.00001f) { 。。。 } else if(abs(y-100.0f)<0.00001f)//走这条吗?-100 - 100 =? { 。。。 } 另外对于浮点数求绝对值最好用fabs
wzuxian2012 2013-02-14
  • 打赏
  • 举报
回复
引用 6 楼 dubiousway 的回复:
1. double 和 float ,天差地别。 ------- float x=0; //这里用double ,结果可是不一样多了。 int n=1000; while(n--) x+=0.1; printf("%f\n",x); ---------- 2。 1.5和0.5, 自然是不一样的精度。我也只是给个例子说。
x = 99.999046 0.1*1000==100才对 fabs(1000-99.999046)==0.000954f>0.00001f 这如何解释???? 不是执行了1000次循环后,x的值应该和100接近嘛? 如果接近,那么绝对值应该小于0.00001f才对 难道是因为 x+=0.1;这里没使用x+=0.1f??导致的。 好了,我修改一下: 依然如此, 如何解释这个问题呢
加载更多回复(5)
课程背景Modbus 协议是工业自动化控制系统中常见的通信协议,协议的全面理解是个痛点。本课程主讲老师集10多年在Modbus协议学习、使用中的经验心得,结合当前物联网浪潮下Modbus协议开发的痛点,推出这套面向Modbus 协议初学者的课程。本课程不同于以往市面课程只是协议讲解无实现代码,而是采用讲解与实践并重的方式,结合STM32F103ZET6开发板进行手把手编程实践,十有利于初学者学习。涵盖了学习者在Modbus协议方面会遇到的方方面面的问题,是目前全网首个对Modbus协议进行全面总结的课程。课程名称   协议讲解及实现>>课程内容1、Modbus 协议的基础。2、Modbus协议栈函数编程实现。3、Modbus协议在串行链路编程实现。4、Modbus协议在以太网链路编程实现。5、常见问题的解决方法。带给您的价值通过学习本课程,您可以做到如下:1、全面彻底的理解Modbus协议。2、理解在串行链路,以太网链路的实现。3、掌握Modbus协议解析的函数编程方法,调试工具的使用。4、掌握多个串口,网口同时运行同一个Modbus协议栈的方法。5、掌握Modbus协议下,负数,浮点数等处理方法。讲师简介许孝刚,山东大学工程硕士,副高职称,技术总监。10多年丰富嵌入式系统开发经验,国家软考“嵌入式系统设计师”。2017年获得“华为开发者社区杰出贡献者”奖励。

69,371

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧