搜索
bottom↓
回复: 30

关于运算精度的问题

[复制链接]

出0入0汤圆

发表于 2017-3-23 20:52:20 | 显示全部楼层 |阅读模式
电脑上的算法,全部是float类型的数据。

把算法移植到stm32f4上面,f4上面也是float类型的数据。

运算结果的精度,F4就是没有电脑上面好啊,怎么回事?有没有什么解决办法?

阿莫论坛20周年了!感谢大家的支持与爱护!!

阿莫论坛才是最爱国的,关心国家的经济、社会的发展、担心国家被别国牵连卷入战争、知道珍惜来之不易的和平发展,知道师夷之长,关注世界的先进文化与技术,也探讨中国文化的博大精深,也懂得警惕民粹主义的祸国殃民等等等等,无不是爱国忧民的表现。(坛友:tianxian)

出0入0汤圆

发表于 2017-3-23 21:05:02 | 显示全部楼层
double float

出0入0汤圆

 楼主| 发表于 2017-3-24 08:18:48 | 显示全部楼层

电脑上也是float

出0入0汤圆

发表于 2017-3-24 08:35:05 来自手机 | 显示全部楼层
电脑算float时,先转成double.但是f4的fpu是单精度的。所以,硬件不一样啊。

出0入0汤圆

发表于 2017-3-24 09:00:13 | 显示全部楼层
c语言要求运算float时, 先转成double, 算完了再转会float

电脑可以实现这一点,f4下不能, 因为fpu是float, 不是double

出0入0汤圆

发表于 2017-3-24 09:20:17 | 显示全部楼层
TBG3 发表于 2017-3-24 09:00
c语言要求运算float时, 先转成double, 算完了再转会float

电脑可以实现这一点,f4下不能, 因为fpu是flo ...

是不是定义为double float  双精度型就可以了啊?

出0入0汤圆

发表于 2017-3-24 09:20:40 | 显示全部楼层
TBG3 发表于 2017-3-24 09:00
c语言要求运算float时, 先转成double, 算完了再转会float

电脑可以实现这一点,f4下不能, 因为fpu是flo ...

是不是定义为double float  双精度型就可以了啊?

出0入0汤圆

发表于 2017-3-24 09:20:57 | 显示全部楼层
单精度的吧,运算位数不够。

出0入0汤圆

发表于 2017-3-24 09:35:33 | 显示全部楼层
河图洛书 发表于 2017-3-24 09:20
是不是定义为double float  双精度型就可以了啊?

f4的fpu是单精度的,x86的fpu是双精度的,这是硬件,无可改变。但是也可以用单精度fpu算双精度,不过算的时间比单精度就很长了。

出0入0汤圆

 楼主| 发表于 2017-3-24 09:54:33 | 显示全部楼层
TBG3 发表于 2017-3-24 09:00
c语言要求运算float时, 先转成double, 算完了再转会float

电脑可以实现这一点,f4下不能, 因为fpu是flo ...

c语言要求运算float时, 先转成double, 算完了再转会float

谢谢,请问这个哪里有正式的文档说明啊?

出0入0汤圆

 楼主| 发表于 2017-3-24 09:56:57 | 显示全部楼层
TBG3 发表于 2017-3-24 09:00
c语言要求运算float时, 先转成double, 算完了再转会float

电脑可以实现这一点,f4下不能, 因为fpu是flo ...

c语言要求运算float时, 先转成double, 算完了再转会float

谢谢,请问这个哪里有正式的文档说明啊?

出0入0汤圆

发表于 2017-3-24 10:09:11 | 显示全部楼层
justforfun 发表于 2017-3-24 09:56
c语言要求运算float时, 先转成double, 算完了再转会float

谢谢,请问这个哪里有正式的文档说明啊? ...

C89,C99规范。或者度娘自动类型转换

出0入0汤圆

 楼主| 发表于 2017-3-24 10:38:23 | 显示全部楼层
TBG3 发表于 2017-3-24 10:09
C89,C99规范。或者度娘自动类型转换

谢谢,但对于每一个数据,我前面给了float类型定义,数据的后面还加了" f "的后缀啊。

C规范里面是说
The type is determined by the suffix; F or f makes it float, L or l makes it long double, otherwise it is double.   

出0入0汤圆

发表于 2017-3-24 10:41:22 | 显示全部楼层
justforfun 发表于 2017-3-24 10:38
谢谢,但对于每一个数据,我前面给了float类型定义,数据的后面还加了" f "的后缀啊。

C规范里面是说

这是常量表达,不是运算时自动类型转换。

出0入0汤圆

 楼主| 发表于 2017-3-24 10:48:41 | 显示全部楼层
TBG3 发表于 2017-3-24 10:41
这是常量表达,不是运算时自动类型转换。

啊,运算的时候,还是会自动转成double类型啊?

出0入0汤圆

发表于 2017-3-24 10:57:29 | 显示全部楼层
justforfun 发表于 2017-3-24 10:48
啊,运算的时候,还是会自动转成double类型啊?

对啊,就跟int以下的变量,运算时,自动升到int一样啊。

出0入0汤圆

 楼主| 发表于 2017-3-24 10:58:52 | 显示全部楼层
TBG3 发表于 2017-3-24 10:41
这是常量表达,不是运算时自动类型转换。

有没有什么办法在编译器里面做设置,禁止自动类型转换?

出0入0汤圆

发表于 2017-3-24 11:11:18 | 显示全部楼层
justforfun 发表于 2017-3-24 10:58
有没有什么办法在编译器里面做设置,禁止自动类型转换?

你用汇编写吧。先看看x86的浮点是否支持float运算,然后可以试试用单浮点运算指令来运算。

出0入0汤圆

 楼主| 发表于 2017-3-24 11:57:27 | 显示全部楼层
TBG3 发表于 2017-3-24 11:11
你用汇编写吧。先看看x86的浮点是否支持float运算,然后可以试试用单浮点运算指令来运算。 ...

这么麻烦啊。。。

难道没有简单轻松的办法来禁止类型自动转换么?

出0入0汤圆

发表于 2017-3-24 17:23:49 | 显示全部楼层
justforfun 发表于 2017-3-24 11:57
这么麻烦啊。。。

难道没有简单轻松的办法来禁止类型自动转换么?

为何要禁止自动类型转换?禁止了你ARM上的计算精度也不会变高啊。

出0入0汤圆

 楼主| 发表于 2017-3-24 18:15:02 | 显示全部楼层
flamma 发表于 2017-3-24 17:23
为何要禁止自动类型转换?禁止了你ARM上的计算精度也不会变高啊。

在电脑上禁止类型自动转换,这样可以对比电脑上的结果和stm32上面的结果,看看是不是禁止类型自动转换以后,结果就一模一样了。

出0入0汤圆

发表于 2017-3-24 18:21:31 | 显示全部楼层
本帖最后由 flamma 于 2017-3-24 18:25 编辑
justforfun 发表于 2017-3-24 18:15
在电脑上禁止类型自动转换,这样可以对比电脑上的结果和stm32上面的结果,看看是不是禁止类型自动转换以 ...


这个就要看你用电脑用的什么编译器了。大多数编译器可以用 /Op 或 /fltconsistency编译选项来关闭优化。

许多 C++ 编译器提供了“一致性”浮点模型(通过 /Op 或 /fltconsistency 开关),从而使开 发人员能够创建符合严格浮点语义的程序。采用该模型时,可以防止编译器对浮点计算使用大多数优 化,同时允许其对非浮点代码使用这些优化。但是,该一致性模型具有一个缺点。为了在不同的 FPU 体系结构中返回可预测的结果,几乎所有 /Op 实现都将中间表达式舍入到用户指定的精度;例如,考 虑下面的表达式:
float a, b, c, d, e;
. . .
a = b*c + d*e;
为了在使用 /Op 开关时产生一致的且可重复的结果,该表达式的计算方式按如下方式实现:
float x = b*c;
float y = d*e;
a = x+y;
现在,最终结果在计算该表达式的每一步 中都产生了单精度舍入误差。

出0入0汤圆

 楼主| 发表于 2017-3-24 18:24:46 | 显示全部楼层
flamma 发表于 2017-3-24 18:21
这个就要看你用电脑用的什么编译器了。

visual studio啊,不知道怎样禁止自动类型转换。

出0入0汤圆

发表于 2017-3-24 18:26:04 | 显示全部楼层
justforfun 发表于 2017-3-24 18:24
visual studio啊,不知道怎样禁止自动类型转换。

我楼上回答了。

出0入93汤圆

发表于 2017-3-24 18:34:27 | 显示全部楼层
justforfun 发表于 2017-3-24 11:57
这么麻烦啊。。。

难道没有简单轻松的办法来禁止类型自动转换么?

有。但是我不知道C/C++有没有这个函数。如果没有,请自行想办法,比如改用C++Builder,

Delphi中浮点数默认使用长达80位的Extended(x86默认的FPU浮点寄存器,C/C++没有这个),比它精度差的还有Double(64位,C/C++的double)、Real48(48位,同样C/C++也没有)、Single(相当于C/C++的float,32位)。默认情况下,各种语言中Delphi的浮点数是很精确的,但是慢。所以Delphi提供了Set8087CW改变这些行为,同时也用这个函数设定是不是抛出浮点异常。

对于Single类型:Set8087CW(Default8087CW and $FCFF)
对于Double类型:Set8087CW((Default8087CW and $FCFF) or $0200)
对于extended类型:Set8087CW(Default8087CW or $0300)

出0入93汤圆

发表于 2017-3-24 18:42:13 | 显示全部楼层
flamma 发表于 2017-3-24 18:21
这个就要看你用电脑用的什么编译器了。大多数编译器可以用 /Op 或 /fltconsistency编译选项来关闭优化。
...

你这个不行的,就算每一步分别计算,比如float x = b * c; 还是会进行类型提升,等价于float x = (float)((double)b * (double)c),使得结果更为精确。
好像C语言的float就没有规定运算规则,它只能类型提升为double后才能运算。

真要改变这种状态,那就用C++重新定义一个类,比如Float,然后重载它的各种运算,软件完成IEEE 754 单精度浮点运算。

出0入0汤圆

 楼主| 发表于 2017-3-24 20:01:26 | 显示全部楼层
flamma 发表于 2017-3-24 18:21
这个就要看你用电脑用的什么编译器了。大多数编译器可以用 /Op 或 /fltconsistency编译选项来关闭优化。
...

这太复杂了,代码有几万行,里面很多计算,手工一个一个的改,怎么改得过来?

出0入0汤圆

 楼主| 发表于 2017-3-24 20:03:10 | 显示全部楼层
takashiki 发表于 2017-3-24 18:42
你这个不行的,就算每一步分别计算,比如float x = b * c; 还是会进行类型提升,等价于float x = (float) ...

谢谢,定义一个class,有用吗?还是需要定义一个namespace ?

出0入0汤圆

发表于 2017-3-24 20:26:40 | 显示全部楼层
本帖最后由 flamma 于 2017-3-24 20:41 编辑
takashiki 发表于 2017-3-24 18:42
你这个不行的,就算每一步分别计算,比如float x = b * c; 还是会进行类型提升,等价于float x = (float) ...


并不会。这个编译器选项是不会主动提升的。为了能中途提升,微软的VS还专门提供了几个其他编译选项。

另外,你这种说法也不对。浮点数在计算机里的表达是符号位+指数位+有效数位,float和double的指数位差别造成了最大值范围的差别,有效数位的差别造成了精确值的差距。float x = b * c,b和c就算提升成为double只不过多填充了几个0bit而已,相乘后再转换成为float进位精度又丢失了,并不会造成差距。

出0入93汤圆

发表于 2017-3-24 22:22:29 | 显示全部楼层
flamma 发表于 2017-3-24 20:26
并不会。这个编译器选项是不会主动提升的。为了能中途提升,微软的VS还专门提供了几个其他编译选项。

另 ...

我测试了VC2008 Release、Debug模式和STVD,可能是不知道 /Op或/fltconsistency到底放哪儿,我放在附加选项里面,它会提示忽略未知选项。浮点模型选/fp:strict,仍然会进行类型提升

测试:随便找了个1234567890,变成float丧失精度结果只能表述为1234567936,就用它好了,整数,好算。
代码:
  1. float f = 1234567936;
  2. float rslt = f * f * f * f;
  3. double drslt = f * f * f * f;
复制代码

真实结果:1234567936 * 1234567936 * 1234567936 * 1234567936 = 2323057574211064201414294301065609216
VC2008得出结果:rslt = 2323057509722296200000000000000000000
                          drslt = 2323057574211064200000000000000000000
手动分步计算:     rslt = 2323057668178621200000000000000000000
STVD得出结果:   rslt = 2.32305767e+036

可见drslt还是很精确的,只是转换成float时丧失精度了,分步计算和STVD的结果是一致的。

出0入0汤圆

发表于 2017-3-25 17:30:41 | 显示全部楼层
takashiki 发表于 2017-3-24 22:22
我测试了VC2008 Release、Debug模式和STVD,可能是不知道 /Op或/fltconsistency到底放哪儿,我放在附加选 ...

https://msdn.microsoft.com/zh-cn/library/aa984742(v=vs.71).aspx
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-17 00:21

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表