本文翻译自:Why does changing the sum order returns a different result?

Why does changing the sum order returns a different result? 为什么更改总和顺序会返回不同的结果?

23.53 + 5.88 + 17.64 = 47.05 23.53 + 5.88 + 17.64 = 47.05

23.53 + 17.64 + 5.88 = 47.050000000000004 23.53 + 17.64 + 5.88 = 47.050000000000004

Both Java and JavaScript return the same results. Java和JavaScript都返回相同的结果。

I understand that, due to the way floating point numbers are represented in binary, some rational numbers ( like 1/3 - 0.333333... ) cannot be represented precisely. 我理解,由于浮点数用二进制表示的方式,一些有理数( 如1/3 - 0.333333 ...... )无法精确表示。

Why does simply changing the order of the elements affect the result? 为什么简单地改变元素的顺序会影响结果?


#1楼

参考:https://stackoom.com/question/1LAAD/为什么更改总和顺序会返回不同的结果


#2楼

I believe it has to do with the order of evaulation. 我认为这与评估的顺序有关。 While the sum is naturally the same in a math world, in the binary world instead of A + B + C = D, it's 虽然数学世界中的总和自然是相同的,但在二进制世界中,而不是A + B + C = D,它就是

A + B = E
E + C = D(1)

So there's that secondary step where floating point numbers can get off. 所以浮动点数可以下降的第二步。

When you change the order, 当您更改订单时

A + C = F
F + B = D(2)

#3楼

Maybe this question is stupid, but why does simply changing the order of the elements affects the result? 也许这个问题很愚蠢,但为什么简单地改变元素的顺序会影响结果呢?

It will change the points at which the values are rounded, based on their magnitude. 它将根据其大小更改值舍入的点。 As an example of the kind of thing that we're seeing, let's pretend that instead of binary floating point, we were using a decimal floating point type with 4 significant digits, where each addition is performed at "infinite" precision and then rounded to the nearest representable number. 作为我们所看到的那种事物的一个例子,让我们假设我们使用的是带有4位有效数字的十进制浮点类型,而不是二进制浮点数,其中每次加法都以“无限”精度执行,然后舍入到最近的可表示数字。 Here are two sums: 这是两个总和:

1/3 + 2/3 + 2/3 = (0.3333 + 0.6667) + 0.6667= 1.000 + 0.6667 (no rounding needed!)= 1.667 (where 1.6667 is rounded to 1.667)2/3 + 2/3 + 1/3 = (0.6667 + 0.6667) + 0.3333= 1.333 + 0.3333 (where 1.3334 is rounded to 1.333)= 1.666 (where 1.6663 is rounded to 1.666)

We don't even need non-integers for this to be a problem: 我们甚至不需要非整数来解决这个问题:

10000 + 1 - 10000 = (10000 + 1) - 10000= 10000 - 10000 (where 10001 is rounded to 10000)= 010000 - 10000 + 1 = (10000 - 10000) + 1= 0 + 1= 1

This demonstrates possibly more clearly that the important part is that we have a limited number of significant digits - not a limited number of decimal places . 这可能更清楚地表明,重要的部分是我们有限数量的有效数字 - 不是有限的小数位数 If we could always keep the same number of decimal places, then with addition and subtraction at least, we'd be fine (so long as the values didn't overflow). 如果我们总能保持相同的小数位数,那么至少加上和减法,我们就没事了(只要值没有溢出)。 The problem is that when you get to bigger numbers, smaller information is lost - the 10001 being rounded to 10000 in this case. 问题是当你得到更大的数字时,更小的信息会丢失 - 在这种情况下,10001被舍入到10000。 (This is an example of the problem that Eric Lippert noted in his answer .) (这是Eric Lippert在答案中提到的问题的一个例子。)

It's important to note that the values on the first line of the right hand side are the same in all cases - so although it's important to understand that your decimal numbers (23.53, 5.88, 17.64) won't be represented exactly as double values, that's only a problem because of the problems shown above. 重要的是要注意右侧第一行的值在所有情况下都是相同的 - 所以尽管理解您的十进制数(23.53,5.88,17.64)不能完全表示为double精度值很重要,由于上面显示的问题,这只是一个问题。


#4楼

Floating point numbers are represented using the IEEE 754 format, which provides a specific size of bits for the mantissa (significand). 浮点数使用IEEE 754格式表示,该格式为尾数(有效数字)提供特定的位大小。 Unfortunately this gives you a specific number of 'fractional building blocks' to play with, and certain fractional values cannot be represented precisely. 不幸的是,这会为您提供特定数量的“分数构建块”,并且某些小数值无法精确表示。

What is happening in your case is that in the second case, the addition is probably running into some precision issue because of the order the additions are evaluated. 在您的情况下发生的情况是,在第二种情况下,由于评估添加的顺序,添加可能会遇到一些精确问题。 I haven't calculated the values, but it could be for example that 23.53 + 17.64 cannot be precisely represented, while 23.53 + 5.88 can. 我没有计算出这些值,但可能是23.53 + 17.64不能精确表示,而23.53 + 5.88可以。

Unfortunately it is a known problem that you just have to deal with. 不幸的是,这是一个已知的问题,你只需要处理。


#5楼

This actually covers much more than just Java and Javascript, and would likely affect any programming language using floats or doubles. 这实际上涵盖的不仅仅是Java和Javascript,并且可能会影响使用浮点数或双精度的任何编程语言。

In memory, floating points use a special format along the lines of IEEE 754 (the converter provides much better explanation than I can). 在内存中,浮点使用IEEE 754的特殊格式(转换器提供了比我更好的解释)。

Anyways, here's the float converter. 无论如何,这是浮动转换器。

http://www.h-schmidt.net/FloatConverter/ http://www.h-schmidt.net/FloatConverter/

The thing about the order of operations is the "fineness" of the operation. 关于操作顺序的事情是操作的“精细度”。

Your first line yields 29.41 from the first two values, which gives us 2^4 as the exponent. 你的第一行从前两个值得到29.41,这给了我们2 ^ 4作为指数。

Your second line yields 41.17 which gives us 2^5 as the exponent. 你的第二行产生41.17,它给出了2 ^ 5作为指数。

We're losing a significant figure by increasing the exponent, which is likely to change the outcome. 我们通过增加指数而失去一个重要的数字,这可能会改变结果。

Try ticking the last bit on the far right on and off for 41.17 and you can see that something as "insignificant" as 1/2^23 of the exponent would be enough to cause this floating point difference. 尝试勾选最右边的最后一位开启和关闭41.17,你可以看到像指数的1/2 ^ 23那样“无关紧要”的东西足以引起这个浮点差异。

Edit: For those of you who remember significant figures, this would fall under that category. 编辑:对于那些记得重要人物的人来说,这属于那个类别。 10^4 + 4999 with a significant figure of 1 is going to be 10^4. 具有显着数字1的10 ^ 4 + 4999将是10 ^ 4。 In this case, the significant figure is much smaller, but we can see the results with the .00000000004 attached to it. 在这种情况下,有效数字要小得多,但我们可以看到附加了.00000000004的结果。


#6楼

Here's what's going on in binary. 这是二进制文件中发生的事情。 As we know, some floating-point values cannot be represented exactly in binary, even if they can be represented exactly in decimal. 众所周知,某些浮点值无法用二进制精确表示,即使它们可以用十进制精确表示。 These 3 numbers are just examples of that fact. 这3个数字只是这个事实的例子。

With this program I output the hexadecimal representations of each number and the results of each addition. 使用此程序,我输出每个数字的十六进制表示和每个加法的结果。

public class Main{public static void main(String args[]) {double x = 23.53;   // Inexact representationdouble y = 5.88;    // Inexact representationdouble z = 17.64;   // Inexact representationdouble s = 47.05;   // What math tells us the sum should be; still inexactprintValueAndInHex(x);printValueAndInHex(y);printValueAndInHex(z);printValueAndInHex(s);System.out.println("--------");double t1 = x + y;printValueAndInHex(t1);t1 = t1 + z;printValueAndInHex(t1);System.out.println("--------");double t2 = x + z;printValueAndInHex(t2);t2 = t2 + y;printValueAndInHex(t2);}private static void printValueAndInHex(double d){System.out.println(Long.toHexString(Double.doubleToLongBits(d)) + ": " + d);}
}

The printValueAndInHex method is just a hex-printer helper. printValueAndInHex方法只是一个十六进制打印机助手。

The output is as follows: 输出如下:

403787ae147ae148: 23.53
4017851eb851eb85: 5.88
4031a3d70a3d70a4: 17.64
4047866666666666: 47.05
--------
403d68f5c28f5c29: 29.41
4047866666666666: 47.05
--------
404495c28f5c28f6: 41.17
4047866666666667: 47.050000000000004

The first 4 numbers are x , y , z , and s 's hexadecimal representations. 前4个数字是xyzs的十六进制表示。 In IEEE floating point representation, bits 2-12 represent the binary exponent , that is, the scale of the number. 在IEEE浮点表示中,位2-12表示二进制指数 ,即数字的比例。 (The first bit is the sign bit, and the remaining bits for the mantissa .) The exponent represented is actually the binary number minus 1023. (第一位是符号位, 尾数的其余位。)表示的指数实际上是二进制数减去1023。

The exponents for the first 4 numbers are extracted: 提取前4个数字的指数:

    sign|exponent
403 => 0|100 0000 0011| => 1027 - 1023 = 4
401 => 0|100 0000 0001| => 1025 - 1023 = 2
403 => 0|100 0000 0011| => 1027 - 1023 = 4
404 => 0|100 0000 0100| => 1028 - 1023 = 5

First set of additions 第一组添加

The second number ( y ) is of smaller magnitude. 第二个数字( y )的幅度较小。 When adding these two numbers to get x + y , the last 2 bits of the second number ( 01 ) are shifted out of range and do not figure into the calculation. 当添加这两个数字以获得x + y ,第二个数字( 01 )的最后2位移出范围并且不计入计算。

The second addition adds x + y and z and adds two numbers of the same scale. 第二个加法添加x + yz并添加两个相同比例的数字。

Second set of additions 第二组补充

Here, x + z occurs first. 这里,首先出现x + z They are of the same scale, but they yield a number that is higher up in scale: 它们具有相同的比例,但它们产生的数字在规模上更高:

404 => 0|100 0000 0100| => 1028 - 1023 = 5

The second addition adds x + z and y , and now 3 bits are dropped from y to add the numbers ( 101 ). 第二个加法添加x + zy ,现在从y中删除3位以添加数字( 101 )。 Here, there must be a round upwards, because the result is the next floating point number up: 4047866666666666 for the first set of additions vs. 4047866666666667 for the second set of additions. 这里,必须有一个圆形向上,因为结果是下一个浮点数起来: 4047866666666666对于首套增加的对4047866666666667第二组补充的。 That error is significant enough to show in the printout of the total. 该错误足以显示在总数的打印输出中。

In conclusion, be careful when performing mathematical operations on IEEE numbers. 总之,在对IEEE数字执行数学运算时要小心。 Some representations are inexact, and they become even more inexact when the scales are different. 一些表示是不精确的,当尺度不同时,它们变得更加不精确。 Add and subtract numbers of similar scale if you can. 如果可以,添加和减去相似比例的数字。

为什么更改总和顺序会返回不同的结果?相关推荐

  1. 联想计算机BIOS启动顺序boot,如何进入BIOS并更改启动顺序?

    吴川 华南区技术负责人 概要 在更换硬盘或迁移系统等情况下,大家可能需要进入BIOS并更改启动顺序(即从指定的硬盘/分区启动,比如USB设备或CD驱动器等),下面将介绍不同电脑品牌的BIOS快捷键以及 ...

  2. mysql 从a到z 查询_mysql 查询数据时按照A-Z顺序排序返回结果集

    mysql 查询数据时按照A-Z顺序排序返回结果集 $sql = "SELECT * , ELT( INTERVAL( CONV( HEX( left( name, 1 ) ) , 16, ...

  3. 按照从右向左的阅读顺序,返回一个不含重复数字的新的整数

    描述 输入一个 int 型整数,按照从右向左的阅读顺序,返回一个不含重复数字的新的整数. 保证输入的整数最后一位不是 0 . 数据范围:1≤n≤10的8次方 输入描述: 输入一个int型整数 输出描述 ...

  4. ubuntu with linux 3.19,ubuntu更改启动顺序

    在ubuntu中修改启动配置. 启动相关grub2主要包含下面三个文件:1.   /boot/grub/grub.cfg 文件    2.   /etc/grub.d/ 文件夹   3.   /etc ...

  5. PLSQL已建成表后更改列名顺序

    PLSQL已建成表后更改列名顺序更改 解决方法 思路为:依据旧表选择正确列名顺序创建新表,依据正确的新表替换旧表 create table A as select field1,field3,fiel ...

  6. python更改数据框指定位置的数据_python – 移动数据框列并更改列顺序

    我有一个名为df的数据框,其中包含以下列数据标题: date A B C D E F G H I 07/03/2016 2.08 1 NaN NaN 1029 2 2.65 4861688 -0.03 ...

  7. 微星B450M安装ubuntu 18.04 BIOS更改启动顺序

    为了给新组装的台式机装个双系统,先用rufus烧录一个安装u盘,一般我选的是MBR,这次也没有改,然后格式化那里选的是FAT32,这个比较通用,安装的时候要分区,分区就是分了efi,根目录以及swap ...

  8. 在BIOS中如何更改启动顺序

    http://zhidao.baidu.com/link?url=UCUSCZnP8OoLqNHtYICz4yfm-IuECzzS3VtjWSMUhKMI9OsvJHb8U0FEcWCIlR0qu1U ...

  9. 细琢磨,try catch finally 执行顺序与返回值

    try catch finally 常见格式如下: try{//应用代码}catch(Exception e){//异常捕捉处理}finally{//资源释放.流关闭等等 } 通常执行顺序: try有 ...

最新文章

  1. 公司的API接口被刷了,那是因为你没这样做
  2. mongo 创建索引_索引系列:2dsphere索引
  3. Java中的回调函数学习-深入浅出
  4. 厦大诚招青年AI科学家:待遇不一般,连配偶和子女教育都考虑到了
  5. 两个sql交集_简单明了的sql基础语句
  6. 只开窗不镀锡_推拉窗和平开窗哪个好?
  7. php中sha1,PHP中sha1()函数和md5()函数的绕过
  8. 结晶紫行业调研报告 - 市场现状分析与发展前景预测
  9. mysql 07001_MySQL迁移文件的小问题
  10. Python穷举法破解密码
  11. 【吹水阁】为什么微信红包单次上限200,不限制次数?——微信红包、转账支付宝转账
  12. java转Js原生,Java到JavaScript的转换
  13. 几行代码,把你的小电影全部藏好了!
  14. 90后首次购房心路历程
  15. 我们为什么教不好自己的孩子?(云中逸客)
  16. 计算机网络并行传输和串行传输,网线RJ45是并行传输还是串行
  17. python通过qq邮箱发邮件
  18. java md5加密长度_java中使用MD5加密算法进行加密
  19. php单链表检测有没有环,PHP找出链表中环入口节点步骤详解
  20. tar 解压缩命令~

热门文章

  1. javaweb中运用fileupload上传文件
  2. 如何使用 vimdiff 来 git diff /svn diff
  3. 从Exchange 通往Office 365系列(五)部署MailBox服务器
  4. [论文收集] ICWS 2008论文
  5. 机器学习项目实战----泰坦尼克号获救预测(一)
  6. Centos 添加swap
  7. Android时间戳与字符串相互转换
  8. 高性能javascript读书笔记(三.DOM 编程2)
  9. Redhat6.5安装vnc服务远程桌面
  10. F-Scrack 弱口令检测脚本