小浮点大名堂,1.0元累加2000万次等于2000万吗? float的下溢
Posted 飞凡可期
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小浮点大名堂,1.0元累加2000万次等于2000万吗? float的下溢相关的知识,希望对你有一定的参考价值。
//============================================================================
// Name : p930Add2000wTimes.cpp
// Author : perfey
// Version :
// Copyright : Your copyright notice
// Description : 将浮点数1,累加2000万次,得到的还是2000万吗?, Ansi-style
//============================================================================
#include <iostream>
using namespace std;
int main() {
cout << "!!!Terriable World!!!" << endl; // prints !!!Hello World!!!
float price = 1.0f;
int N = 2e7;
float sum = 0;
for (int i = 0; i < N; i++)
sum += price;
cout<< " Total output : 2000 * 1.0f = "<< sum << " Gap is "<< N - sum << endl;
for (int i = 0; i < N; i++)
sum += price;
cout<< "2 Add 200w time agin, output : 2000 * 1.0f = "<< sum << " Gap is "<< N - sum << endl;
cout << " 这就意味着,float类型加2个任意数,超出1678万倍的数相当于没有加!!!! 浮点未必比定点好,工程化突破了思维认知了吧!"<<endl;
//原因是float是32bits,规划结构是(1符号位、8个指数为、23个有效位)。实际有效位还包括最前面的1,所以有效位是24bit,但一个大数和一个小浮点数相加
//时,会首先指数拉齐,这个拉齐在我们case中就是不停右移1.0,一直右移超过24位(指数超过24)导致1.0移位到0,下限溢出。。。 增加了0;
/* Kahan Summation算法解决它, 用一个小量c存下了被移位掉的小值;*/
float res = 0.0f;
float sumK = 0.0f;
for (int i= 0; i < N ; i++)//16777217 = 2**24
{
float tmp = price - res;
float sumt = sumK + tmp;
res = sumt-sumK - tmp;
sumK = sumt;
}
cout << "3 output of Kahan Summation: "<< sumK << " Gap is "<< N - sum <<" res=" << res << endl;
//注意到Kahan Summation算法并非万能,只是用一个临时变量保住了丢失值,其实本体还可能有损耗的。。。别玩了check res中值;(比如1677217+偶数)
/*当前,最好方法是把小数进位到分、厘,用long来转载,怎么也不会溢出的:如下*/
int pricef = 100;
long sumL = 0;
for (int i = 0; i < N; i++)
sumL += pricef;
float sumF = sumL / 100;
cout <<" 4 长整形转换的输出: sum = "<< sumF <<endl;
return 0;
}
output
1 Total output : 2000 * 1.0f = 1.67772e+007 Gap is 3.22278e+006
2 Add 200w time agin, output : 2000 * 1.0f = 1.67772e+007 Gap is 3.22278e+006
这就意味着,float类型加2个任意数,超出1678万倍的数相当于没有加!!!! 浮点未必比定点好,工程化突破了思维认知了吧!
3 output of Kahan Summation: 2e+007 Gap is 3.22278e+006 res=0
4 长整形转换的输出: sum = 2e+007
说明
- 1.0元累加2000万次,发现只有1.67千万,少了320万去了哪儿? 根因是float构成是(1符号位,8个指数位,23个有效数位),因为第24位必然是1所以对于非0数,实际有效位24位,最大值是16777216,大约1677万; 故相差这么大的数累加,会导致小的数右移到0,导致下溢垮掉!
- 举个栗子:上面代码,当sum=16777216时(0,127+24,0),而被加数1是(0,127,0),[注意127指数表示指数部分0,指数部分0时表示最小的浮点数2^-127(非正式的)]对齐指数,导致
- https://www.h-schmidt.net/FloatConverter/IEEE754.html 自己玩玩试试看
- 第2行输出用特殊算法缓解了下溢出问题,但没有根治,浮点残余值res在很多条件还会有残余。但是好歹保留了大数。
- 第3行是直接化货币,从分为单位,直接用Long计算,不再有下溢出问题。。。上溢出得到2**63次方(正负9223372036854775808)。9千亿亿分,90亿亿元,全球所有年代的GDP衡量也够了(正常货币)。
以上是关于小浮点大名堂,1.0元累加2000万次等于2000万吗? float的下溢的主要内容,如果未能解决你的问题,请参考以下文章