有点难的float运算改int运算,结果有时差1

My_Love 2013-07-10 11:09:22
主要是CPU不支持硬件float运算;

int i,j的范围都是-128到127;

int x = i*1.402; 完全等价 int x = i*1402/1000;

int y = i*1.402+j; 有时不等价 int y = i*1402/1000+j;
发现不等价时 i*1.402+j后改变了符号!

有什么好的算法能解决这个问题?

...全文
361 20 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
c090869 2013-07-11
  • 打赏
  • 举报
回复
原来y=i*1.402+j 是加j后取整; y=(i*1402)/1000+j是加j前取整;可能造成相反方向取整。 y=i*1.402+j 可以改成 y=i*1.402; y=y+j;两个语句; 或者 y=i*1402/1000+j 改成 y=(i*1402+j*1000)/1000 好像也可以。
橡木疙瘩 2013-07-11
  • 打赏
  • 举报
回复
验证过了:

#include <stdio.h>

int main()
{
    int i, j;
    int success = 1;
    for( i = -128; i < 128; ++ i)
    {
        for( j = -128; j < 128; ++ j)
        {
            int y1 = (int)( i * 1.402 + j);
            int y2 = (i * 1402 + j * 1000) / 1000;
            //上面就是用整数运算实现的(int)( i * 1.402 + j)
            if( y1 != y2 )
            {
                printf( "\n%d * 1.402 + %d = %d\n(%d * 1402 + %d * 1000) / 1000 = %d\n", i, j, y1, i, j, y2 );
                success = 0;
            }
        }
    }
    if( success )
    {
        printf("(int)( i * 1.402 + j) ≡ (i * 1402 + j * 1000) / 1000)\n");
    }
    getchar();
}
输出为:

(int)( i * 1.402 + j) ≡ (i * 1402 + j * 1000) / 1000)

橡木疙瘩 2013-07-11
  • 打赏
  • 举报
回复
我理解错了。楼主说“int i,j范围是-128到128……改变了符号”,我错以为是溢出。 既然没有溢出,那么用“y = (i * 1402 + j * 1000) / 1000”计算应该可以解决问题。
My_Love 2013-07-11
  • 打赏
  • 举报
回复
引用 4 楼 u010936098 的回复:
i*1402溢出了。 1000也溢出了 你需要实现一个高精度数 如果条件允许的话,改用无符号数可也可以让int y = i*1.402+j与 int y = i*1402/1000+j等价,这时前者其实是(unsigned int)(i * 1.402 + j) % 256;而后者是 ((i%256)*(1402%256)/(1000%256))%256,与(i*1402/1000)%256安全等价。而用带符号数的关键是溢出时符号改变,而符号的改变会影响带符号数乘除法操作的逻辑。
-128~127是i,j的范围,int是4字节,怎么可能溢出!
引用 6 楼 adlay 的回复:
忽略一点精度把, 把 1402/1000 用一个分子分母都在 256 以内的一个近似值来代替, 比如 这里就近似成 14 / 10 然后 int y = i*1402/1000+j; 变成 int y = i * 14 / 10 + j; 这样可以保证中间结果不会超出两字节的范围.
这个精度真的不可以忽略...
引用 9 楼 my_live_123 的回复:
int类型怎么可能是-128到127。从它诞生起都至少16位的,到现在32位或64位,都不可能是8位的
看前面的解释,我语文没问题吧?
引用 12 楼 my_live_123 的回复:
vc反汇编结果

5:        int i = 65535,j = 65535;    
004109A8   mov         dword ptr [ebp-4],0FFFFh
004109AF   mov         dword ptr [ebp-8],0FFFFh
6:
7:        int x = i*1.402;// 
004109B6   fild        dword ptr [ebp-4]
004109B9   fmul        qword ptr [__real@8@3fffb374bc6a7ef9d800 (00426030)]//用的fmul
004109BF   call        __ftol (00401208)
004109C4   mov         dword ptr [ebp-0Ch],eax
8:        int y = i*1.402+j; 
004109C7   fild        dword ptr [ebp-4]
004109CA   fmul        qword ptr [__real@8@3fffb374bc6a7ef9d800 (00426030)]
004109D0   fiadd       dword ptr [ebp-8]
004109D3   call        __ftol (00401208)//有取整的过程
004109D8   mov         dword ptr [ebp-10h],eax
这样楼主应该知道他们处理的差别了吧?
所以才要发帖找解决方法...
mujiok2003 2013-07-10
  • 打赏
  • 举报
回复
数学上: -128 <= i <= 127, -128 <= j <= 127 -179.456 <= i*1.402 <= 178.054 -307.456 <= i*1.402 +j <= 255.054 你的int范围是多少?
www_adintr_com 2013-07-10
  • 打赏
  • 举报
回复
找近似值的过程可以这样, 先看分子大还是分母大, 比如大的是 a, 小的是 b, 然后把大的改为 256, 小的改成 256 * b / a 取整. 对于上面的 1402/1000 就是 256 * 1000 / 1402 = 182.596... 取整就是 183 所以, 可以用 256 / 182 来代替 1402 / 1000.
www_adintr_com 2013-07-10
  • 打赏
  • 举报
回复
忽略一点精度把, 把 1402/1000 用一个分子分母都在 256 以内的一个近似值来代替, 比如 这里就近似成 14 / 10 然后 int y = i*1402/1000+j; 变成 int y = i * 14 / 10 + j; 这样可以保证中间结果不会超出两字节的范围.
www_adintr_com 2013-07-10
  • 打赏
  • 举报
回复
你的 int 大小是 2 个字节吗? 127 * 1402 并不会超出 4 字节的 int 范围的呀.
橡木疙瘩 2013-07-10
  • 打赏
  • 举报
回复
i*1402溢出了。 1000也溢出了 你需要实现一个高精度数 如果条件允许的话,改用无符号数可也可以让int y = i*1.402+j与 int y = i*1402/1000+j等价,这时前者其实是(unsigned int)(i * 1.402 + j) % 256;而后者是 ((i%256)*(1402%256)/(1000%256))%256,与(i*1402/1000)%256安全等价。而用带符号数的关键是溢出时符号改变,而符号的改变会影响带符号数乘除法操作的逻辑。
My_Love 2013-07-10
  • 打赏
  • 举报
回复
引用 1 楼 turingo 的回复:
1、先计算i*1.402; 2、然后把结果截取到char类型范围; 3、最后再+j。
*1.402是不允许的要变换成int运算!
图灵狗 2013-07-10
  • 打赏
  • 举报
回复
1、先计算i*1.402; 2、然后把结果截取到char类型范围; 3、最后再+j。
saiusky21 2013-07-10
  • 打赏
  • 举报
回复
学习了,也遇到过这样的问题,一般都有精度要求吧。
Cisco_zk 2013-07-10
  • 打赏
  • 举报
回复
主要是CPU不支持硬件float运算; int i,j的范围都是-128到127; int x = i*1.402; 完全等价 int x = i*1402/1000; int y = i*1.402+j; 有时不等价 int y = i*1402/1000+j; 发现不等价时 i*1.402+j后改变了符号! 有什么好的算法能解决这个问题?
Ericz 2013-07-10
  • 打赏
  • 举报
回复
计算机不支持浮点数,你这么做,那结果还是一个整形,最后结果还四舍五入了,误差太大了。
引用 楼主 My_Love 的回复:
主要是CPU不支持硬件float运算; int i,j的范围都是-128到127; int x = i*1.402; 完全等价 int x = i*1402/1000; int y = i*1.402+j; 有时不等价 int y = i*1402/1000+j; 发现不等价时 i*1.402+j后改变了符号! 有什么好的算法能解决这个问题?
赵4老师 2013-07-10
  • 打赏
  • 举报
回复
电脑内存或文件内容只是一个一维二进制字节数组及其对应的二进制地址; 人脑才将电脑内存或文件内容中的这个一维二进制字节数组及其对应的二进制地址的某些部分看成是整数、有符号数/无符号数、浮点数、复数、英文字母、阿拉伯数字、中文/韩文/法文……字符/字符串、汇编指令、函数、函数参数、堆、栈、数组、指针、数组指针、指针数组、数组的数组、指针的指针、二维数组、字符点阵、字符笔画的坐标、黑白二值图片、灰度图片、彩色图片、录音、视频、指纹信息、身份证信息……
一根烂笔头 2013-07-10
  • 打赏
  • 举报
回复
vc反汇编结果

5:        int i = 65535,j = 65535;    
004109A8   mov         dword ptr [ebp-4],0FFFFh
004109AF   mov         dword ptr [ebp-8],0FFFFh
6:
7:        int x = i*1.402;// 
004109B6   fild        dword ptr [ebp-4]
004109B9   fmul        qword ptr [__real@8@3fffb374bc6a7ef9d800 (00426030)]//用的fmul
004109BF   call        __ftol (00401208)
004109C4   mov         dword ptr [ebp-0Ch],eax
8:        int y = i*1.402+j; 
004109C7   fild        dword ptr [ebp-4]
004109CA   fmul        qword ptr [__real@8@3fffb374bc6a7ef9d800 (00426030)]
004109D0   fiadd       dword ptr [ebp-8]
004109D3   call        __ftol (00401208)//有取整的过程
004109D8   mov         dword ptr [ebp-10h],eax
9:                            
10:       printf("%d %d\n", x, y);
004109DB   mov         eax,dword ptr [ebp-10h]
004109DE   push        eax
004109DF   mov         ecx,dword ptr [ebp-0Ch]
004109E2   push        ecx
004109E3   push        offset string "x = i*1.402%s" (0042601c)
004109E8   call        printf (004010a0)
004109ED   add         esp,0Ch
11:
12:       x = i*1402/1000;
004109F0   mov         eax,dword ptr [ebp-4]
004109F3   imul        eax,eax,57Ah//用的是imul
004109F9   cdq
004109FA   mov         ecx,3E8h
004109FF   idiv        eax,ecx//用的idiv
00410A01   mov         dword ptr [ebp-0Ch],eax
13:       y = i*1402/1000+j;
00410A04   mov         eax,dword ptr [ebp-4]
00410A07   imul        eax,eax,57Ah
00410A0D   cdq
00410A0E   mov         ecx,3E8h
00410A13   idiv        eax,ecx
00410A15   add         eax,dword ptr [ebp-8]
00410A18   mov         dword ptr [ebp-10h],eax
14:           printf("%d %d\n", x, y);
00410A1B   mov         edx,dword ptr [ebp-10h]
00410A1E   push        edx
00410A1F   mov         eax,dword ptr [ebp-0Ch]
00410A22   push        eax
00410A23   push        offset string "x = i*1.402%s" (0042601c)
00410A28   call        printf (004010a0)
00410A2D   add         esp,0Ch
这样楼主应该知道他们处理的差别了吧?
一根烂笔头 2013-07-10
  • 打赏
  • 举报
回复


#include <stdio.h>

int main(int argc, char **argv)
{
int i = 65535,j = 65535; //范围都是-128到127;

int x = i*1.402;// 完全等价 int x = i*1402/1000;
int y = i*1.402+j; //有时不等价 int y = i*1402/1000+j;
//发现不等价时 i*1.402+j后改变了符号!
printf("%d %d\n", x, y);

x = i*1402/1000;
y = i*1402/1000+j;

printf("%d %d\n", x, y);
return 0;
}


发现他们是等价

编译的时候都会给出警告
图灵狗 2013-07-10
  • 打赏
  • 举报
回复
我的意思是说按照你的自己的方法计算i*1.402,由于类型被扩充了,所以完了你要把结果截取到char类型范围。
引用 3 楼 My_Love 的回复:
[quote=引用 1 楼 turingo 的回复:] 1、先计算i*1.402; 2、然后把结果截取到char类型范围; 3、最后再+j。
*1.402是不允许的要变换成int运算![/quote]
一根烂笔头 2013-07-10
  • 打赏
  • 举报
回复
引用 楼主 My_Love 的回复:
主要是CPU不支持硬件float运算; int i,j的范围都是-128到127; int x = i*1.402; 完全等价 int x = i*1402/1000; int y = i*1.402+j; 有时不等价 int y = i*1402/1000+j; 发现不等价时 i*1.402+j后改变了符号! 有什么好的算法能解决这个问题?
int类型怎么可能是-128到127。从它诞生起都至少16位的,到现在32位或64位,都不可能是8位的

70,020

社区成员

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

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