工作小数点数

Posted

技术标签:

【中文标题】工作小数点数【英文标题】:working decimal point numbers 【发布时间】:2013-12-10 12:08:03 【问题描述】:

我用java写了一个关于浮点的程序,但是得到的输出不准确;而用 C 编写的相同程序确实产生了准确的结果。

代码片段:

public class One 

public static void main(String...args)

    double i, num=0.0;
    for(i=0;i<10;i++)
    num=num+0.1;
    System.out.println(num);

    

输出:0.9999999999999999

C语言的程序是这样的

#include<stdio.h>
main()


double i,num=0.0;
for(i=0;i<10;i++)
num=num+0.1;

printf("%f",num);


输出:1.00000

我做错了什么?

【问题讨论】:

为什么你没有被 c 标记? How to make strings from floating-point numbers with '.' (dot) as a decimal separator and not more than 3 digits after floating point?的可能重复 您是否尝试过在 Java 中使用等效的 printf?你在比较苹果和橘子。 【参考方案1】:

C 隐藏了问题而不是删除它

C 程序更准确的想法是对正在发生的事情的误解。两者都有不精确的答案,但默认情况下 C 已四舍五入为 6 个有效数字。 隐藏微小的错误。如果您在计算中实际使用该值(例如num==1),您会发现两者都不准确。

通常,写得好的程序并不重要

一般来说,智能编写的程序可以毫无困难地处理这个微小的错误。例如,您的程序可以重写为每次重新创建一个双精度数

double i, num=0.0;
for(i=0;i<10;i++)
   num=0.1*i;
   System.out.println(num);


意味着错误不会增长。此外,您永远不应该将 == 与双打一起使用,因为在那里可以看到微小的不准确。

在非常非常偶然的情况下,这个微小的错误是一个问题(货币程序是最常见的);可以使用 bigDecimal。

这不是浮点数的问题,而是从基数 10 到基数 2 的转换。

以 10 为底的分数无法精确表达;例如 1/3。类似地,有些分数不能用二进制精确表示,例如 1/10。 It is from this perspective that you should look at this

在这种情况下的问题是,当你写“0.1”时,一个以 10 为基数的数字,计算机必须永远将其转换为二进制数,0.1=(binary)0.00011001100110011001100110011001.......,但因为它不能用空格精确地表示二进制数它最终成为(binary)0.0001100110011001。二进制友好数字(例如 1/2)将完全准确,直到双精度数字用完(此时即使二进制友好数字也无法精确表示)

1小数位数不准确

【讨论】:

【参考方案2】:

尝试使用BigDecimal。浮点数表示是 Java 的一个古老的争论点(不仅如此)。

double i;
BigDecimal num = BigDecimal.ZERO;
for(i=0;i<10;i++)
    num = num.add(new BigDecimal("0.1"));
System.out.println(num);

【讨论】:

这是一个奇怪的情况,java 是诚实的,而 c 试图隐藏它,因此 java 被认为不太准确,sigh【参考方案3】:

问题不在于 Java,而在于计算机如何用二进制表示浮点数。有些数字无法准确表示,因此在添加0.1 时会出现一点舍入误差。我建议您阅读this great article 以获得详细说明。此外,还有algorithms 专门设计用于在添加数字序列时最大限度地减少错误。

【讨论】:

【参考方案4】:

这是floating 数字表示中的一个问题。使用BigDecimal

您可以在这里理解问题。如果你运行以下代码,

    double i, num=0.0;
    for(i=0;i<10;i++) 
        System.out.println(num);
        num=num+0.1;
    
    System.out.println(num);

输出

0.0
0.1
0.2
0.30000000000000004 // wrong value, it should be 0.3
0.4
0.5
0.6
0.7
0.7999999999999999 // this one should be 0.7+0.1=0.8
0.8999999999999999 // this should be 0.8+0.1=0.9
0.9999999999999999 // final result should be 0.9+0.1=1

让我们试试BigDeciaml

 double i;
 BigDecimal num=BigDecimal.ZERO;
 for(i=0;i<10;i++) 
     System.out.println(num);
     num=num.add(new BigDecimal("0.1"));
   
 System.out.println(num);

输出

0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1.0

【讨论】:

【参考方案5】:

有一个strictfp关键字,比如:

strictfp class Example

    //your code here

strictfp 是在 Java SE 2(或附近)中引入的关键字,因为浮点模型放宽了一点。

【讨论】:

您能解释一下strictfp 的含义/作用 通过我的阅读,strictfp 确保所有系统上的等效浮点行为,而不是使用 base-10 数字系统而不是 base-2 @RichardTingle Base-10 与浮点无关。 strictfp 只是通过任何必要的手段来确保 IEEE754 合规性,例如使用软件而不是 FP 指令。 @EPJ 这是我的观点,strictfp 不会影响这个问题;在以 2 为底的系统中表示以 10 为底的数字(浮点最终会导致问题,但它是以 10 为底的 --> 以 2 为底的数字,这是大部分问题所在)

以上是关于工作小数点数的主要内容,如果未能解决你的问题,请参考以下文章

用C语言怎么实现浮点数的四舍五入,保留两位小数点

如果是浮点数则显示小数点 2 位,如果是整数则显示小数点数 0 位 [重复]

C语言编程中怎么自动去掉浮点数小数点后没用的0。

支持小数点和小数点逗号的字符串到浮点数转换

C语言如何实现输出浮点数小数点两位(四舍五入法),但若小数点最后位为零则舍去??

怎么精确提取一个浮点数的小数部分?