浮点数引起的错误

Posted spacerunnerZ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浮点数引起的错误相关的知识,希望对你有一定的参考价值。

一、浮点数精度

Float 为单精度,内存中占 4 个字节,有效数位是 7 位。

double为 双精度,占 8 个字节,有效数位是 16 位。

 

二、常见错误

1. 不能存储全部有效数字

#include <iostream>

int main()

    float f 0.123456789f ;
    std::cout << f << \'\\n\';

    return 0;

打印:0.123457

 

2. 采用比较运算符时

#include <iostream>

int main()

    double d1 100.0 - 99.99 ; // should equal 0.01 mathematically
    double d2 10.0 - 9.99 ; // should equal 0.01 mathematically

    if (d1 == d2)
        std::cout << "d1 == d2" << \'\\n\';
    else if (d1 > d2)
        std::cout << "d1 > d2" << \'\\n\';
    else if (d1 < d2)
        std::cout << "d1 < d2" << \'\\n\';

    return 0;

d1和d2理应一致,但是打印结果为:d1 > d2

如果用Debugger来看,d1 = 0.0100000000000005116 ,d2 = 0.0099999999999997868,这导致了出现了意料之外的结果。

通常来说,用比较运算符是对的,但是当用于比较的两个数非常接近时,可能出现错误。

 

3. 使用==和!=时

注意!一定不要在比较浮点数时使用==,因为只有当两个数完全一模一样是,才会返回true,否则即使最小的舍入误差都会导致结果为false。

看以下错误出现的例子:

#include <iostream>

int main()

    double d 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 ; // should sum to 1.0

    if (d == 1.0)
        std::cout << "equal\\n";
    else
        std::cout << "not equal\\n";

    return 0;

 

打印结果是:not equal

 

所以,我们可以用下面这个代码来替代==,

#include <cmath> // for std::abs()

// epsilon is an absolute value
bool approximatelyEqualAbs(double a, double b, double absEpsilon)

    // if the distance between a and b is less than absEpsilon, then a and b are "close enough"
    return std::abs(a - b) <= absEpsilon;

 

还有一些更严谨的方法,但是实现复杂度也更高,具体查看该网页拓展部分:https://www.learncpp.com/cpp-tutorial/relational-operators-and-floating-point-comparisons/  

 

4. 整数除法

我们对两个整数做除法操作,我们想要的是一个浮点数,但是得到的确会是一个整数。

#include <iostream>

int main()

    int x 5 ;
    int y 3 ;

    std::cout << x << " divided by " << y << " is: " << x / y << \'\\n\'; // integer division

    return 0;

 打印结果为:5 divided by 3 is: 1

我么可以用static_cast函数将其中一个整数转为浮点数,最后得到的结果就是浮点数。或者写成5.0/7.0。

js,java,浮点数运算错误及应对方法

js,java,浮点数运算错误及应对方法

一,浮点数为什么会有运算错误

IEEE 754 标准规定了计算机程序设计环境中的二进制和十进制的浮点数自述的交换、算术格式以及方法.

 

现有存储介质都是2进制。2进制的进制基数是2,那么一个数字只要被因素包含大于2的质数的数除,都会产生无限循环小数。无限循环小数和无理数都无法,和非无限循环的有理数一起用同一种方式存储到存储介质上的同时还保持计算的兼容性。
对于无限循环小数,可以设计一种格式存储到介质上,但是同时又要和非无限循环的有理数能够计算,效率应该会变得非常低下。 对于无理数,小数位的数字没有规律可循,所以根本无法保存精确值,只能保存近似值。
高精度计算,一般可以将数字转发成string, 去掉小数点,按位计算再保存回string,再加回小数点。

 

二,错误举例

document.write (0.01 + 0.05);  //输出结果为0.060000000000000005

document.write (0.09 + 0.01);  //输出结果为0.09999999999999999

document.write(11*22.9);     //输出结果为251.89999999999998

 

三,js如何应对

扩大倍数法:有多少位小数就扩大10的n次方

document.write((0.01*100+0.09*100)/100); //输出结果为0.1

四舍五入法:

document.write((0.01+0.09).toFixed(2)); //保留2位小数,输出结果为0.10

document.write(Math.round((0.01+0.09)*100)/100); //输出结果为0.1

 

四,java如何应对

扩大倍数法:有多少位小数就扩大10的n次方

System.out.println((0.01*100+0.09*100)/100); //输出结果为0.1

四舍五入法:

System.out.println(Math.rint(0.01*100+0.09*100)/100); //输出结果为0.1

System.out.println(Math.round(0.01*100+0.09*100)/100); //输出结果为0.1

System.out.println(new  BigDecimal(0.099).setScale(2,  RoundingMode.HALF_UP).doubleValue());  //输出结果为0.10

 


备注: Java支持7中舍入法

1、 ROUND_UP:远离零方向舍入。向绝对值最大的方向舍入,只要舍弃位非0即进位。

2、 ROUND_DOWN:趋向零方向舍入。向绝对值最小的方向输入,所有的位都要舍弃,不存在进位情况。

3、 ROUND_CEILING:向正无穷方向舍入。向正最大方向靠拢。若是正数,舍入行为类似于ROUND_UP,若为负数,舍入行为类似于ROUND_DOWN。Math.round()方法就是使用的此模式。

4、 ROUND_FLOOR:向负无穷方向舍入。向负无穷方向靠拢。若是正数,舍入行为类似于ROUND_DOWN;若为负数,舍入行为类似于ROUND_UP。

5、 HALF_UP:最近数字舍入(5进)。这是我们最经典的四舍五入。

6、 HALF_DOWN:最近数字舍入(5舍)。在这里5是要舍弃的。

7、 HAIL_EVEN:银行家舍入法。银行家舍入法.

 

 

 

 

以上是关于浮点数引起的错误的主要内容,如果未能解决你的问题,请参考以下文章

js,java,浮点数运算错误及应对方法

Android greenDAO 存储浮点数错误

PySpark:列的绝对值。类型错误:需要浮点数

Numpy 错误“无法将字符串转换为浮点数:'Illinois'”

从错误中学python————字符串转浮点数

图像数据无法转换为浮点数错误?