double精度缺失解决方案.docx
《double精度缺失解决方案.docx》由会员分享,可在线阅读,更多相关《double精度缺失解决方案.docx(6页珍藏版)》请在冰豆网上搜索。
![double精度缺失解决方案.docx](https://file1.bdocx.com/fileroot1/2022-12/13/c0d3cc43-e3e8-4af4-8dc2-f924f56d22b4/c0d3cc43-e3e8-4af4-8dc2-f924f56d22b41.gif)
double精度缺失解决方案
double精度缺失解决方案
篇一:
Java中解决浮点型数据精度不准的问题
Java中如何解决double和float精度不准的问题我们知道浮点数是无法在计算机中准确表示的,例如在计算机中只是表示成了一个近似值,因此,对付点数的运算时结果具有不可预知性。
在进行数字运算时,如果有double或float类型的浮点数参与计算,偶尔会出现计算不准确的情况。
如以下示例代码:
[java]viewplaincopyprint?
1.packageex;
2.
3.publicclassBigDeciTest{
4.publicstaticvoidmain(String[]args){
(+);
();
(*100);
(/100);
9.
10.}
11.
12.}
上述代码执行结果如下:
[plain]viewplaincopyprint?
1.
2.
3.
4.
在大多数情况下,使用double和float计算的结果是准确的,但是在一些精度要求很高的系统中,这种问题是非常严重的。
在《EffectiveJava》中提到一个原则,那就是float和double只能用来作科学计算或者是工程计算,但在商业计算中我们要用,通过使用BigDecimal类我们可以解决上述问题,实例代码如下:
[java]viewplaincopyprint?
1.packageex;
2.
3.import*;
4.
5.publicclassBigDecimalDemo{
6.publicstaticvoidmain(String[]args){
((,));
((,));
((,100));
((,100));
11.}
12.}
13.
14.classArithUtil{
15.privatestaticfinalintDEF_DIV_SCALE=10;
16.
17.privateArithUtil(){}
18.
19.publicstaticdoubleadd(doubled1,doubled2){
b1=newBigDecimal((d1));
b2=newBigDecimal((d2));
(b2).doubleValue();
23.
24.}
25.
26.publicstaticdoublesub(doubled1,doubled2){
b1=newBigDecimal((d1));
b2=newBigDecimal((d2));
(b2).doubleValue();
30.
31.}
32.
33.publicstaticdoublemul(doubled1,doubled2){
b1=newBigDecimal((d1));
b2=newBigDecimal((d2));
(b2).doubleValue();
37.
38.}
39.
40.publicstaticdoublediv(doubled1,doubled2){
41.
div(d1,d2,DEF_DIV_SCALE);
43.
44.}
45.
46.publicstaticdoublediv(doubled1,doubled2,intscale){
(scale 48.thrownewIllegalArgumentException("Thescalemustbeaposit
iveintegerorzero");
49.}
b1=newBigDecimal((d1));
b2=newBigDecimal((d2));
(b2,scale,_HALF_UP).doubleValue()
;
53.
54.}
55.
56.}
运行结果如下:
[plain]viewplaincopyprint?
1.
2.
3.
4.
详细,请参考API文档。
篇二:
C#浮点数精度丢失问题
C#浮点数精度丢失问题
C#中的浮点数,分单精度(float)和双精度(double):
float是的别名,介于-和+之间的32位数字,符合二进制浮点算法的IEC60559:
1989(IEEE754)标准;
double是的别名,介于-和+之间的64位数字,符合二进制浮点算法的IEC60559:
1989(IEEE754)标准;
我们知道,计算机只认识0和1,所以数值都是以二进制的方式储存在内存中的。
(对于人脑和计算机哪个聪明,个人更倾向于选择人脑,计算机只是计算得快,而且不厌其烦而已!
)
所以要知道数值在内存中是如何储存的,需先将数值转为二进制(这里指在范围内的数值)。
根据IEEE754标准,任意一个二进制浮点数V均可表示为:
V=(-1^s)*M*(2^e)。
其中s∈{0,1};M∈[1,2);e表示偏移指数。
以(10)为例,先转成二进制的数值为:
(2)(截取16位小数),采用科学记数法等于*(2^17)(整数位是1),即(10)=(-1^0)**(2^17)。
整数部分可采用"除2取余法",小数部分可采用"乘2取整法"。
从结果可以看出,小数部分转为二进制后,小数位数超过16位(我已经手算到小数点后32位都还没算完,其实这个位数是无穷尽的)。
由于无法得到完全正确的数值,这里就引申出浮点数精度丢失的问题:
/*程序段1*/
floatnum_a=;
floatnum_b=num_a/2;
(num_a);
(num_b);
这段程序代码,我们预想中正确的结果应该是:
和。
但结果居然是!
!
!
原因下面将讲到...
这里介绍另一种转小数部分的方法,有兴趣可以看下:
假如结果要求精确到N位小数,那么只需要将小数部分乘以2的N次方(例如N=16,*(2^16),得到)。
取整数部分(12451),按整数的方法转为二进制,得到11000010100011,不足N位在高位用0补足。
结果精确到16位后,用二进制表示为。
可以看出,若是小数部分乘以2的N次方后,可以得到一个整数,那么这个小数可以用二进制精确表示,否则则不可以。
(原理很简单,根据二进制小数位转十进制的方法,反推回去就可以得到这个结果)
在内存中,float和double的储存格式是一致的,只是占用的空间大小不同。
float总共占用32位:
从左往右,第1位是符号位,占1位;第2-9位是指数位,占8位;第10-32位是尾数位,占23位。
double总共占用64位,从左往右第1位也是符号位,占1位;第2-12位是指数位,占11位;第13-64位是尾数位,占52位。
其中,符号位(即上文的s,下同),0代表正数,1代表负数。
对于float,8位指数位的值范围为0-255(10),由于指数(即上文的e,下同)可正可负,而指数位的值是一个无符号整数。
根据标准规定,储存时采用偏移值(偏移值为127)的方法,储存值为指数+127。
例如01110011
(2)表示指数-12(10)((-12)+127=115),10001011
(2)表示指数12(10)(12+127=139)。
{
另外,IEEE754规定(同样适用于double):
当指数全为0时,如果尾数全为0,表示±0(正负取决于符号位),如果尾数不全为0,计算时指数等于-126,尾数不加上第一位的1,而是还原为的小数,表示更接近0的小数;
当指数全为1时,如果尾数全为0,表示±无穷大(正负取决于符号位),如果尾数不全为0,表示这不是一个数(NaN)。
}
同样的,对于double,11位指数位,储存时采用的偏移值为1023。
尾数位,由于所有数值均可以转换成*(2^N)(此处暂时忽略精度问题),所以尾数部分只保存小数部分(最高位的1不存入内存,提高1个位的精度)。
以float为例,二进制为*(2^17);数值为正数,符号位是0;
指数是17,保存为144(17+127=144),即10010000(共8位,不足8位在高位用0补足);
小数位是10000100011110111001100(截取23位);
最终得到:
01001000010000100011110111001100,按字节倒序顺序,转为十六进制就是:
CC3D4248
floatf_num=;
varf_bytes=(f_num);
("float:
");
((f_bytes));
(("",f_(i=>(i,2).PadLeft(8,'0'))));
同样的格式,double最终得到:
0100000100001000010001111011100110000101000111101011100001010010(最后两位结果为什么是10而不是01,请参考浮点数的舍入),按字节倒序顺序,转为十六进制就是:
52B81E85B9470841
doubled_num=;
vard_bytes=(d_num);
("double:
");
((d_bytes));
(("",d_(i=>(来自:
小龙文档网:
double精度缺失解决方案)ring(i,2).PadLeft(8,'0'))));
回到精度丢失的问题,由于小数位无法算尽,内存用截取精度的方式储存了转换后的二进制,这导致保存的结果并非是完全正确的数值。
看回程序段1的例子,
num_a在内存中其实是保存为:
01001000010000100011110111001100,换算成十进制就是:
;
num_b在内存中其实是保存为:
01000111110000100011110111001100,换算成十进制就是:
;
先看num_b,由于num_a在内存中储存的值已经是不正确的,那么再利用其进行计算,得到的结果%也会是不正确的。
所以num_b的结果并不是我们想要的。
然后为什么会变成,而会变成呢?
我们知道,内存中确实是储存了和这两个值,那么就只有可能是在输出的时候做了变动。
其实这是微软做的小把戏,我们有句俗话说"以毒攻毒",大概就这个意思,既然储存的已经是不正确的数值,那么在输出的时候,会智能地猜测判断原先正确的数值是什么,然后输出猜测的那个值,说不定就真的猜中了呢!
篇三:
float与double的范围和精度=
float与double的范围和精度
1.范围
float和double的范围是由指数的位数来决定的。
float的指数位有8位,而double的指数位有11位,分布如下:
float:
1bit(符号位)8bits(指数位)23bits(尾数位)
double:
1bit(符号位)11bits(指数位)52bits(尾数位)
于是,float的指数范围为-127~+128,而double的指数范围为-1023~+1024,并且指数位是按补码的形式来划分的。
其中负指数决定了浮点数所能表达的绝对值最小的非零数;而正指数决定了浮点数所能表达的绝对值最大的数,也即决定了浮点数的取值范围。
float的范围为-2^128~+2^128,也即-+38~++38;double的范围为-2^1024~+2^1024,也即-+308~++308。
2.精度
float和double的精度是由尾数的位数来决定的。
浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“1”,由于它是不变的,故不能对精度造成影响。
float:
2^23=8388608,一共七位,这意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效
数字;
double:
2^52=4503599627370496,一共16位,同理,double的精度为15~16位。
中Number类型
在Oracle中Number类型可以用来存储0,正负定点或者浮点数,可表示的数据范围在
*10(-130)——..9*10(125){38个9后边带88个0}
的数字,当Oracle中的数学表达式的值>=*10(126)时,Oracle就会报错。
Number的数据声明如下:
表示作用说明
Number(p,s)声明一个定点数p(precision)为精度,s(scale)表示小数点右边的数字个数,精度最大值为38,
Number(p)声明一个整数相当于Number(p,0)Number声明一个浮点数其精度为38,要注意的是scale的值没有应用,也就是说scale的指不能简单的理解为0,或者其他的数。
定点数的精度(p)和刻度(s)遵循以下规则:
?
当一个数的整数部分的长度>p-s时,Oracle就会报错?
当一个数的小数部分的长度>s时,Oracle就会舍入。
?
当s(scale)为负数时,Oracle就对小数点左边的s个数字
进行舍入。
?
当s>p时,p表示小数点后第s位向左最多可以有多少位数字,如果大于p则Oracle报错,小数点后s位向右的数字被舍入
4.验证
createorreplacefunctionfunc_test(p_typenumber)returnnumber
is
/*
功能:
基于警度图数据同步
*/
l_cntnumber;
begin
selectp_typeintol_cntfromdual;
returnl_cnt;
endfunc_test;
/
showerr;
5.结论
number的总长度是40位,其中可能包括:
小数点,负号位。
select
to_char(func_test(-
1234567891234))fromdual;
-//包括小数点及负号位共40位
select
to_char(func_test())fromdual;
//4位整数+小数点+35位小数=40位
select
to_char(func_test())fromdual;
//3位整数+小数点+36位小数=40位
select
to_char(func_test(1234567891234567891234567891234567891234))fromdual;
1234567891234567891234567891234567891234//40位整数
select
to_char(func_test(12345678912345678912345678912345678912345))fromdual;
+40//41
位时精度发生丢失
×10^40即12345678912345678912345678912345678900000