IO/ACM中来自浮点数的陷阱(收集向)

  OI/ACM中经常要用到小数来解决问题(概率、计算几何等),但是小数在计算机中的存储方式是浮点数而不是我们在作数学运算中的数,有精度的限制。

  以下以GUN C++为准,其他语言(或编译器)也差不了多少。本文竞赛向。

一、基础篇

1、一般浮点数使用double,范围为大概为-10^308 ~ 10^308,有效精度为15~16位10进制数。

2、一般没事(比如内存问题)不用float,而使用double,一个double占8个字节。

3、信息学竞赛一般使用scanf和printf输入输出,而浮点数的输入是scanf("%lf", &x),浮点数的输出是printf("%f", x),注意两个是不一样的。使用printf("%lf", x)来输出浮点数有可能会出错,因为标准中规定double输出是使用"%f"的,并非所有编译器都实现了"%lf"的输出(比如POJ的G++就没有)。

4、在实在没有办法的情况下可以使用long double,但标准只规定了其精度一定不小于double,有可能等于double请注意。

5、double的精度有限,并不是精确的存储数字,而且它是二进制存储,如下图,0.1 * 10不等于1,因为0.1在计算机中不是我们认为的1/10,下图有0.1在double下的值,可以看出并不完全等于0.1。

  

6、为了解决上面不能直接比较的问题,我们可以设置一个精度EPS,来辅助我们检验两个数是否非常接近。EPS的设置要看题目具体情况,一般情况下可以设成1e-8。

定义函数:

inline int sgn(double x) {
    if(fabs(x) < EPS) return 0;
    return x > 0 ? 1 : -1;
}

用上述函数来判断一个数是否大于小于或等于0。

x = 0 写成 sgn(x) == 0

x < 0 写成 sgn(x) < 0

x > 0 写成 sgn(x) > 0

x > y 写成 sgn(x - y) > 0

x == y 写成 sgn(x - y) == 0

x >= y 写成 sgn(x - y) >= 0

以此类推。

7、上述函数的一种常数优化的写法:

inline int sgn(double x) {
    return (x > EPS) - (x < -EPS);
}

二、进阶篇(陷阱篇)

1、在竞赛中,可能存在给一个double多次计算后,非常接近0的情况,但是,它可能是-0.000000000000000001(一下子举不出实际的例子……),这时输出会变成-0.00,在一部分精确比较的题目上可能会出错,解决方案为输出的时候加上一个EPS(当然不能排除出题人自己煞笔的情况……),即printf("%f", ans + EPS)。

2、如果一个double,要把一个数组里的浮点数累加起来(即 double sum = accumlate(arr.begin(), arr.end(), 0)),如果数组里的数相差可能会比较大,应该从小到大累加。否则可能会发生加了一个很大的浮点数之后,再加一个很小的浮点数,因为精度的原因,跟没加一样(比如一个极端的例子,1e100 + 1e-100还是等于1e100)。因为比较小的数多了,还是会影响到答案的,并不是可以简简单单被忽略的。

3、在计算一个数减去一组数的时候(即double sum = x - accumlate(arr.begin(), arr.end(), 0)),应该先把数组里的数全加起来,再用那个数来减。否则跟上面一样,可能会出现减去了一个很小的数,跟没减一样。

4、二分的时候,EPS设置不当,可能会出现奇怪的问题(特别是有多次二分而对着两次二分要求的精度不一样的时候),这时可以采取另一种写法:

double binary_search(double l, double r) {
    for(int i = 0; i < 100; ++i) {
        double mid = (l + r) / 2;
        if(check(mid)) l = mid;
        else r = mid;
    }
    return l;
}

循环次数可按需设置,一般100都够,对时间要求高可以设小一点。

5、在连乘的时候,浮点数可能会丢比较多的精度,此时可以利用公式x1 * x2 * …… * xn = exp(ln(x1 * x2 * …… * xn)) = exp(ln(x1) + ln(x2) + ……ln(xn)),取他们的对数相加,再求次幂。

比如在计算阶乘的时候,可以:

double Factorial(int n) {
    double res = 0;
    for(int i = 1; i <= n; ++i)
        res += log(i);
    return exp(res);
}

当然有必要的时候(其实是大多时候)我们可以先不exp()先返回,作完后续运算再exp()

6、待补充

时间: 2024-08-24 12:39:53

IO/ACM中来自浮点数的陷阱(收集向)的相关文章

ACM中的浮点数精度处理

在ACM中,精度问题非常常见.其中计算几何头疼的地方一般在于代码量大和精度问题,代码量问题只要平时注意积累模板一般就不成问题了.精度问题则不好说,有时候一个精度问题就可能成为一道题的瓶颈,让你debug半天都找不到错误出在哪. 1.浮点数为啥会有精度问题: 浮点数(以C/C++为准),一般用的较多的是float, double. 占字节数 数值范围 十进制精度位数 float 4 -3.4e-38-3.4e38 6~7 double 8 -1.7e-308-1.7e308 14~15 如果内存不

[ACM]计算几何_浮点数相关

1.多用double少用float double的输入与输出:(注意占位符:scanf中是%lf,printf中是%f) 1 double x; 2 scanf("%lf",&x); 3 printf("%f,x); 2.判断浮点数大于0/小于0/等于0 使用sgn(x): 1 #define EPS (1e-8) 2 inline int sgn(double x){ 3 return (x > EPS) - (x < -EPS); 4 } 3.判断两个

ACM中java的使用 (转)

ACM中java的使用 这里指的java速成,只限于java语法,包括输入输出,运算处理,字符串和高精度的处理,进制之间的转换等,能解决OJ上的一些高精度题目. 1. 输入: 格式为:Scanner cin = new Scanner (new BufferedInputStream(System.in)); 例程: import java.io.*; import java.math.*; import java.util.*; import java.text.*; public class

【转】ACM中java的使用

原文博客:http://www.cnblogs.com/XBWer/archive/2012/06/24/2560532.html ACM中java的使用 这里指的java速成,只限于java语法,包括输入输出,运算处理,字符串和高精度的处理,进制之间的转换等,能解决OJ上的一些高精度题目. 1. 输入: 格式为:Scanner cin = new Scanner (new BufferedInputStream(System.in)); 例程: import java.io.*; import

Java中的BigInteger在ACM中的应用

Java中的BigInteger在ACM中的应用 在ACM中的做题时,经常会遇见一些大数的问题,这是当我们用C或是C++时就会觉得比较麻烦,就想有没有现有的现有的可以直接调用的BigInter,那样就方便很多啦.在java中就有的,所以在这儿我就做一个简要的介绍吧 -:在java中的基本头文件(java中叫包) import java.io.* importjava.util.*       我们所用的输入scanner在这个包中 importjava.math.*          我们下面要

[原创]浅谈JAVA在ACM中的应用

由于java里面有一些东西比c/c++方便(尤其是大数据高精度问题,备受广大ACMer欢迎),所以就可以灵活运用这三种来实现编程,下面是我自己在各种大牛那里总结了一些,同时加上自己平时遇到的一些java上面的东西,像结构体排序什么的都有添加进去,博客一直会在更新,对初学者还是有一些帮助的,大牛们就可以忽略了,如果博客有什么问题,欢迎指出! java中的输出a+b import java.io.*; import java.util.*; public class Main { public st

【技巧】freopen()函数在ACM中的应用

在做题目的过程当中,我们需要在本机上调试,当然我们可以把测试用例一遍一遍粘贴复制,也经常会遇到测试用例很多的时候,输入和输出混了,还要去找输出和题目当中的对照.另外,有的时候题目给的测试用例太少,需要自己或者队友给想几个临界条件,自己动手多添加几个测试用例,每测一遍都要动手输入的话麻烦又浪费时间.这时候freopen()上场了. 函数名:    freopen() 函数声明:  FILE  *freopen(const char *path,const char *mode,FILE *stre

ACM中常用算法----字符串

ACM中常用算法--字符串 ACM中常用的字符串算法不多,主要有以下几种: Hash 字典树 KMP AC自动机 manacher 后缀数组 EX_KMP SAM(后缀自动机) 回文串自动机 下面来分别介绍一下: 0. Hash 字符串的hash是最简单也最常用的算法,通过某种hash函数将不同的字符串分别对应到不同的数字.进而配合其他数据结构或STL可以做到判重,统计,查询等操作. #### 字符串的hash函数: 一个很简单的hash函数代码如下: ull xp[maxn],hash[max

ACM中的正则表达式

layout: post title: ACM中的正则表达式 author: "luowentaoaa" catalog: true mathjax: true tags: - 正则表达式 总结 正则表达式 , 又称规则表达式 , 英文名为 Regular Expression , 在代码中常简写为 regex , regexp 或 RE , 是计算机科学的一个概念 ; 正则表通常被用来检索 , 替换那些符合某个模式 (规则) 的文本 ; 正则表达式是对字符串 (包括普通字符 , 例如