합계 순서를 변경하면 다른 결과가 반환되는 이유는 무엇입니까?
합계 순서를 변경하면 다른 결과가 반환되는 이유는 무엇입니까?
23.53 + 5.88 + 17.64
= 47.05
23.53 + 17.64 + 5.88
= 47.050000000000004
Java와 JavaScript 모두 동일한 결과를 반환합니다.
부동소수점수를 2진수로 표현하는 방법 때문에 일부 유리수(예: 1/3~0.3333...)를 정확하게 표현할 수 없는 것으로 알고 있습니다.
단순히 요소의 순서를 변경하는 것이 결과에 영향을 미치는 이유는 무엇입니까?
어쩌면 이 질문은 바보같을지도 모르지만, 왜 단순히 요소의 순서를 바꾸는 것이 결과에 영향을 미치는가?
그러면 값이 반올림되는 점이 크기에 따라 변경됩니다.예를 들어, 2진수 부동소수점 대신 유효 자릿수가 4자리인 소수점 부동소수점 유형을 사용한다고 가정해 봅시다. 여기서 각 덧셈은 "무한" 정밀도로 수행된 다음 가장 가까운 대표 수치로 반올림됩니다.다음은 두 가지 합계입니다.
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)
이 문제가 되기 위해 정수 이외의 인수는 필요 없습니다.
10000 + 1 - 10000 = (10000 + 1) - 10000
= 10000 - 10000 (where 10001 is rounded to 10000)
= 0
10000 - 10000 + 1 = (10000 - 10000) + 1
= 0 + 1
= 1
이는 소수점 이하 자리 수가 아니라 유효 자릿수가 제한적이라는 것이 중요한 부분임을 더욱 명확하게 보여줍니다.만약 우리가 항상 같은 소수 자릿수를 유지할 수 있다면, 적어도 덧셈과 뺄셈을 한다면, 우리는 괜찮을 것이다(값이 넘치지 않는 한).문제는 큰 숫자가 되면 작은 정보가 손실된다는 것입니다.이 경우 10001은 10000으로 반올림됩니다.(이는 Eric Lippert가 답변에서 언급한 문제의 예입니다.)
첫 .64는 표시되지 않는다는하는 것이 double
을 사용법
바이너리로 진행 중인 내용은 다음과 같습니다.아시다시피 일부 부동소수점 값은 정확하게 10진수로 표시할 수 있더라도 정확히 2진수로 표시할 수 없습니다.이 3개의 숫자는 그 사실의 예에 불과합니다.
이 프로그램으로 각 숫자의 16진수 표현과 각 덧셈의 결과를 출력합니다.
public class Main{
public static void main(String args[]) {
double x = 23.53; // Inexact representation
double y = 5.88; // Inexact representation
double z = 17.64; // Inexact representation
double s = 47.05; // What math tells us the sum should be; still inexact
printValueAndInHex(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);
}
}
printValueAndInHex
도우미일 입니다.
출력은 다음과 같습니다.
403787ae147ae148: 23.53
4017851eb851eb85: 5.88
4031a3d70a3d70a4: 17.64
4047866666666666: 47.05
--------
403d68f5c28f5c29: 29.41
4047866666666666: 47.05
--------
404495c28f5c28f6: 41.17
4047866666666667: 47.050000000000004
번째 는 '4'입니다.x
,y
,z
, , , , 입니다.s
의 16진수 표현입니다.IEEE 부동소수점 표현에서 비트 2-12는 이진수 지수, 즉 숫자의 스케일을 나타냅니다.(첫 번째 비트는 부호 비트이고 나머지 비트는 가수용 비트입니다).표시되는 지수는 실제로는 이진수에서 1023을 뺀 값입니다.
처음 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
첫 번째 추가 세트
숫자 「 」 「 」 )y
을 사용하다를 더하면, 「」가 됩니다.x + y
숫자의 2')01
에 포함되지 않습니다는 범위를 벗어나며 계산에 포함되지 않습니다.
두 번째 추가는 다음과 같습니다.x + y
★★★★★★★★★★★★★★★★★」z
을 사용법
두 번째 추가 세트
서서,,x + z
먼저 발생합니다.같은 규모이지만 규모가 더 높은 수치를 산출합니다.
404 => 0|100 0000 0100| => 1028 - 1023 = 5
두 번째 추가는 다음과 같습니다.x + z
★★★★★★★★★★★★★★★★★」y
3비트가 드롭되었습니다.y
101
에 위쪽으로해야 합니다.4047866666666666
첫 번째 추가 세트의 경우 vs. 4047866666666667
두 번째 추가 세트에 사용할 수 있습니다.그 오류는 전체 출력물에 충분히 나타나 있다.
결론적으로 IEEE 번호에 대한 수학적 연산을 수행할 때는 주의해야 합니다.일부 표현은 부정확하며, 척도가 다르면 훨씬 더 부정확해집니다.가능하면 비슷한 척도의 숫자를 더하고 빼세요.
존의 대답은 물론 옳다.이 경우 오류는 간단한 부동 소수점 연산을 수행할 때 누적되는 오류보다 크지 않습니다.예를 들어 에러가 전혀 발생하지 않는 경우와 작은 에러가 발생하는 경우가 있습니다.이것은 그다지 흥미로운 시나리오가 아닙니다.좋은 질문은 계산 순서를 변경하는 것이 사소한 오류에서 (상대적으로) 엄청난 오류로 바뀌는 시나리오가 있는가 하는 것입니다.대답은 모호하지 않게 '그렇다.
예를 들어 다음과 같습니다.
x1 = (a - b) + (c - d) + (e - f) + (g - h);
대
x2 = (a + c + e + g) - (b + d + f + h);
대
x3 = a - b + c - d + e - f + g - h;
분명히 정확한 산술로는 그들은 같을 것이다.a, b, c, d, e, f, g, h의 값이 x1과 x2 및 x3의 값이 크게 다르도록 찾는 것은 즐거운 일이다.그렇게 할 수 있는지 확인해 봐!
이는 실제로 Java와 Javascript뿐만 아니라 float 또는 double을 사용하는 프로그래밍 언어에도 영향을 미칠 수 있습니다.
메모리에서 부동소수는 IEEE 754에 따라 특별한 형식을 사용합니다(컨버터는 나보다 훨씬 더 나은 설명을 제공합니다).
어쨌든, 여기 플로트 컨버터가 있습니다.
http://www.h-schmidt.net/FloatConverter/
조작 순서는 조작의 「완전성」입니다.
첫 번째 선은 처음 두 값에서 29.41을 산출합니다. 이 값은 2^4를 지수로 제공합니다.
두 번째 선은 41.17을 산출하므로 2^5를 지수로 얻을 수 있습니다.
우리는 지수를 증가시킴으로써 중요한 수치를 잃고 있습니다. 이것은 결과를 바꿀 수 있습니다.
오른쪽 끝의 마지막 비트에 41.17을 체크해 보면 지수의 1/2^23과 같은 "미미한" 것이 이 부동소수점 차이를 일으키기에 충분하다는 것을 알 수 있습니다.
편집: 유의한 수치를 기억하는 분들을 위해 10^4 + 499 의 유의한 숫자 1 은 10^4 가 됩니다.이 경우 유의한 수치는 훨씬 작지만 .000000004가 연결되어 있으면 결과를 알 수 있습니다.
부동소수점 번호는 IEEE 754 형식을 사용하여 표현되며, 이는 가수(시그니컬랜드)에 특정 크기의 비트를 제공합니다.안타깝게도 이 경우 플레이할 수 있는 특정 수의 '프랙셔널 구성 요소'가 제공되며, 특정 분수 값을 정확하게 나타낼 수 없습니다.
이 경우 두 번째 예에서는 추가가 평가되는 순서에 따라 추가에 정밀도 문제가 있을 수 있습니다.값을 계산하지 않았지만, 예를 들어 23.53 + 17.64가 정확하게 표현되지 않는 반면 23.53 + 5.88이 표현될 수 있습니다.
유감스럽게도 그것은 당신이 대처해야 할 알려진 문제입니다.
나는 그것이 에바의 순서와 관련이 있다고 믿는다.수학 세계에서는 합계가 자연히 같지만, 2진수 세계에서는 A + B + C = D 대신, 합계는 다음과 같습니다.
A + B = E
E + C = D(1)
부동소수점 숫자가 내려갈 수 있는 보조 단계가 있습니다.
순서를 바꾸면
A + C = F
F + B = D(2)
여기서 다른 답변에 다른 각도를 더하면 이 SO 답변은 모든 합계 순서가 비트레벨에서 정확히 동일한 값을 반환하는 부동소수점 계산을 수행하는 방법이 있음을 나타냅니다.
언급URL : https://stackoverflow.com/questions/19820297/why-does-changing-the-sum-order-returns-a-different-result
'programing' 카테고리의 다른 글
특정 Unicode 문자의 댓글에서 Java 코드를 실행할 수 있는 이유는 무엇입니까? (0) | 2022.07.24 |
---|---|
data()에서 vuex 상태를 여기에 바인딩하시겠습니까? (0) | 2022.07.24 |
VueJS 플러그인 충돌 (0) | 2022.07.24 |
v-html과 v-text의 차이점은 무엇입니까? (0) | 2022.07.24 |
vueJs의 inside data()와 inside created()의 오브젝트 선언의 차이점은 무엇입니까? (0) | 2022.07.24 |