小浮点大名堂,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的下溢的主要内容,如果未能解决你的问题,请参考以下文章

大数据处理N!(21<N<2000)

算法——动态规划

BZOJ2938: [Poi2000]病毒

从python中的jpeg2000图像中提取元数据

大流量生产环境中的集群节点JS

比特币最新消息今天比特币今日人民币价格挖矿一天能赚2000元比特币美元实时图比特币今天最新实情6亿元的比特币一下归零