부동 소수점에서 두 숫자의 평균에 대한 강력한 계산?


15

하자 x, y두 개의 부동 소수점 숫자합니다. 평균을 계산하는 올바른 방법은 무엇입니까?

순진 방법은 (x+y)/2오버 플로우가 발생할 때 수 xy너무 큽니다. 내 생각 엔 0.5 * x + 0.5 * y아마도 더 나은,하지만 (아마도 비효율적 인)이 곱셈을 포함, 그것은 충분히 좋은 경우에 나는 확실하지 않다. 더 좋은 방법이 있습니까?

내가 놀고있는 또 다른 아이디어는 (y/2)(1 + x/y)if x<=y입니다. 그러나 다시, 나는 이것을 분석하는 방법을 모르겠고 그것이 내 요구 사항을 충족한다는 것을 증명합니다.

또한 계산 된 평균이 >= min(x,y)및 일 것이라는 보장이 필요합니다 <= max(x,y). Don Hatch의 답변 에서 지적 했듯이이 질문을 제기하는 더 좋은 방법은 아마도 가장 정확한 결과를 제공하는 두 숫자의 평균 구현은 무엇입니까? 경우 즉, xy부동 소수점 숫자는 어떻게 부동 소수점 숫자에 가장 가까운을 계산하는 (x+y)/2? 이 경우 계산 평균은 자동 >= min(x,y)이며 <= max(x,y)입니다. 자세한 내용은 Don Hatch의 답변 을 참조하십시오.

참고 : 우선 순위는 강력한 정확성입니다. 효율성이 소모됩니다. 그러나 강력하고 정확한 알고리즘이 많은 경우 가장 효율적인 알고리즘을 선택합니다.


(+1) 놀랍도록 사소한 흥미로운 질문.
Kirill

1
과거에는 부동 소수점 값이 계산되어 중간 결과를 위해 더 정밀한 형식으로 유지되었습니다. a + b (64 비트 배가)가 80 비트 중간 결과를 생성하고 이것이 2로 나뉘어 진 경우 오버플로에 대해 걱정할 필요가 없습니다. 정밀도 손실은 덜 분명합니다.
JDługosz

이것에 대한 해결책은 비교적 간단 해 보입니다 ( 답을 추가했습니다 ). 문제는 컴퓨터 과학 전문가가 아닌 프로그래머 이므로이 질문을 훨씬 더 어렵게 만드는 것은 무엇입니까?
IQAndreas

곱셈과 나눗셈의 비용에 대해 걱정하지 마십시오. 컴파일러가 자동으로 최적화합니다.
Federico Poloni

답변:


18

Higham의 수치 알고리즘정확성과 안정성은 이러한 유형의 문제를 분석 할 수있는 방법을 다루고 있다고 생각 합니다. 2 장, 특히 운동 2.8을 참조하십시오.

이 답변에서 나는 Higham의 책에서 실제로 다루지 않은 것을 지적하고 싶습니다 (그 문제에 대해서는 널리 알려지지 않은 것으로 보입니다). 이와 같은 간단한 수치 알고리즘의 속성을 증명 하려는 경우 Haskell의 sbv 와 같은 패키지를 사용하여 z3 과 같은 최신 SMT 솔버 ( 만족도 모듈로 이론 ) 의 기능을 사용할 수 있습니다 . 이것은 연필과 종이를 사용하는 것보다 다소 쉽습니다.

주어졌고 z = ( x + y ) / 2x z y를 만족 하는지 알고 싶습니다 . 다음 하스켈 코드0xyz=(x+y)/2xzy

import Data.SBV

test1 :: (SFloat -> SFloat -> SFloat) -> Symbolic SBool
test1 fun =
  do [x, y] <- sFloats ["x", "y"]
     constrain $ bnot (isInfiniteFP x) &&& bnot (isInfiniteFP y)
     constrain $ 0 .<= x &&& x .<= y
     let z = fun x y
     return $ x .<= z &&& z .<= y

test2 :: (SFloat -> SFloat -> SFloat) -> Symbolic SBool
test2 fun =
  do [x, y] <- sFloats ["x", "y"]
     constrain $ bnot (isInfiniteFP x) &&& bnot (isInfiniteFP y)
     constrain $ x .<= y
     let z = fun x y
     return $ x .<= z &&& z .<= y

이 작업을 자동으로 수행하겠습니다 . 다음 test1 fun은 IS 명제는 모든 유한 수레 X , Y0 X Y .xfun(x,y)yx,y0xy

λ> prove $ test1 (\x y -> (x + y) / 2)
Falsifiable. Counter-example:
  x = 2.3089316e36 :: Float
  y = 3.379786e38 :: Float

넘친다. 이제 다른 공식을 취한다고 가정 해보십시오 z=x/2+y/2

λ> prove $ test1 (\x y -> x/2 + y/2)
Falsifiable. Counter-example:
  x = 2.3509886e-38 :: Float
  y = 2.3509886e-38 :: Float

점진적 언더 플로로 인해 작동하지 않습니다 ( 모든 산술이 기본 2 이므로 직관적이지 않을 수 있음).(x/2)×2x

이제 :z=x+(yx)/2

λ> prove $ test1 (\x y -> x + (y-x)/2)
Q.E.D.

공장! 는 Q.E.D.A는 증거 것을 test1위에서 정의 된 속성은 모든 수레를 위해 보유하고 있습니다.

동일하지만 ( 0 x y 대신)로 제한되는 것은 어떻습니까?xy0xy

λ> prove $ test2 (\x y -> x + (y-x)/2)
Falsifiable. Counter-example:
  x = -3.1300826e34 :: Float
  y = 3.402721e38 :: Float

자, 넘치면 z = x + ( y / 2 - x / 2 )는 어떻습니까?yxz=x+(y/2x/2)

λ> prove $ test2 (\x y -> x + (y/2 - x/2))
Q.E.D.

그래서 내가 여기에서 시도한 수식 중 가 작동하는 것처럼 보입니다 (증거와 함께). SMT 솔버 접근법은 연필과 종이로 부동 소수점 오류 분석을 수행하는 것보다 간단한 부동 소수점 수식에 대한 의심에 훨씬 빠른 방법으로 보입니다.x+(y/2x/2)

마지막으로 정확성과 안정성의 목표는 종종 성능의 목표와 상충됩니다. 성능을 위해, 나는 컴파일러가 보다 나은 방법을 실제로 보지 못합니다 . 특히 컴파일러는 이것을 기계 명령어로 번역하는 데 많은 노력을 기울이고 있기 때문입니다.(x+y)/2

PS 이것은 모두 단 정밀도 IEEE754 부동 소수점 산술입니다. 나는 검사 배정 밀도 연산 (교체와를 함께 ), 그것도 작동합니다.xx+(y/2x/2)ySFloatSDouble

-ffast-math(x+y)/2

PPPS 조건부없이 간단한 대수적 표현 만 살펴 보았습니다 . 돈 해치공식 은 엄청나게 좋습니다.


2
기다려; x <= y (x> = 0인지 여부에 관계없이)이면 x + (y / 2-x / 2)가 좋은 방법이라고 주장 했습니까? 대답이 정확하게 표현 가능할 때 다음과 같은 경우에는 잘못된 대답을 제공하기 때문에 옳지 않을 것 같습니다. x = -1, y = 1 + 2 ^ -52 (1보다 큰 가장 작은 표현 가능 숫자), 이 경우 대답은 2 ^ -53입니다. 파이썬에서 확인 : >>> x = -1.; y = 1.+2.**-52; print `2**-53`, `(x+y)/2.`, `x+(y/2.-x/2.)`
Don Hatch

2
x(x+y)/2yx,y(x+y)/2(x+y)/2

8

첫째, 모든 경우에 가장 정확한 답변을 제공하는 방법이 있다면 필요한 조건을 만족시킬 것입니다. (주 내가 말할 것을 보다는 가장 정확한 답 가장 정확한 대답은, 이후이 승자가있을 수 있습니다.) 증명 : 만약이, 반대로, 당신은 않는 정확한 것과 같이 가능한 대답이 없다 , 그 요구 조건을 만족을 어느 수단 (케이스가되는 또는 더 않음 모순이다) (케이스가있는 더 않음 모순이다).answer<min(x,y)<=max(x,y)min(x,y)min(x,y)<=max(x,y)<answermax(x,y)

따라서 귀하의 질문이 가장 정확한 답변을 찾는 것으로 요약됩니다. IEEE754 산술 전체를 가정하면 다음을 제안합니다.

if max(abs(x),abs(y)) >= 1.:
    return x/2. + y/2.
else:
    return (x+y)/2.

이것이 가장 정확한 답변을 제공한다는 주장은 다소 지루한 사례 분석입니다. 간다 :

  • 사례 max(abs(x),abs(y)) >= 1.:

    • 하위 케이스 x와 y는 비정규 화되지 않습니다.이 경우 계산 된 답변 x/2.+y/2.은 동일한 가수를 조작하므로 (x+y)/2오버플로를 방지하기 위해 확장 된 지수를 가정하면 산출량 과 정확히 동일한 답변을 제공합니다 . 이 답변은 반올림 모드에 따라 다를 수 있지만 어떤 경우이든 IEEE754는 최상의 답변임을 x+y보장 합니다 (계산 된 값 이 수학 x + y에 가장 가까운 근사값을 보장 한다는 사실에서 2로 나눈 값은 정확합니다) 케이스).
    • 하위 사례 x는 비정규 화되었습니다 abs(y)>=1.

      answer = x/2. + y/2. = y/2. since abs(x/2.) is so tiny compared to abs(y/2.) = the exact mathematical value of y/2 = a best possible answer.

    • 하위 사례 y는 비정규 화되어 abs(x)>=1있습니다.

  • 사례 max(abs(x),abs(y)) < 1.:
    • 계산 된 하위 사례는 x+y비정규 화 또는 비정규 화 및 "짝수"입니다. 계산 x+y이 정확 하지는 않지만 IEEE754에서는 수학 x + y에 대한 가능한 근사값을 보장합니다. 이 경우 식에서 2로 나눈 이후의 (x+y)/2.정확한 나누기 이므로 계산 된 답 (x+y)/2.은 수학 (x + y) / 2에 대한 가장 근사치입니다.
    • 계산 된 하위 사례 x+y는 비정규 화되고 "홀수"입니다.이 경우 정확히 x, y 중 하나는 비정규 화 및 "홀수"여야합니다. 즉, x, y 중 다른 하나는 반대 부호로 비정규 화되어 계산 x+y됩니다. 정확하게 수학 x + y이므로 (x+y)/2.IEEE754는 수학 (x + y) / 2에 대한 가능한 근사값을 계산합니다.

"비정규 화"라고 말했을 때 저는 다른 의미를 지니고 있습니다. 즉, 숫자에 가까울수록 서로 가까운 숫자, 즉 비정규 화 된 숫자 범위의 약 2 배인 숫자의 범위, 즉, 다이어그램의 en.wikipedia.org/wiki/Denormal_number 에있는 처음 8 개의 눈금이 있습니다. 요점은, 이것들의 "홀수"는 그것들을 2로 나누는 것이 정확하지 않은 유일한 숫자라는 것입니다. 이를 명확하게하기 위해이 부분을 다시 말해야합니다.
돈 해치

fl(op(x,y))=op(x,y)(1+δ)|δ|ux/2+y/2(x+y)/2오버 플로우 / 언더 플로우가없는 상태에서 항상 올바르게 반올림되고 남은 것은 오버 플로우 / 언더 플로우를 표시하지 않는 것입니다.
Kirill

@ 키릴 나는 조금 잃어 버렸다 ... 당신은 어디에서 왔습니까? 또한 "2로 나눈 값이 비정상이 아닌 숫자에 대해 정확하다"는 것이 사실이라고 생각하지 않습니다 ... 이것은 내가 넘어간 것과 똑같으며 올바르게 이해하기가 약간 어색한 것 같습니다. 정확한 문장은 "ab / 2 (x)가 최소 비정규 수보다 두 배 이상인 한 x / 2는 정확합니다"와 비슷한 것입니다 ... 아아, 어색합니다!
Don Hatch

3

binary64(이중 정밀도) 계산으로 예시 된 IEEE-754 이진 부동 소수점 형식의 경우 S. Boldo는 아래에 표시된 간단한 알고리즘이 올림 된 평균을 제공함을 공식적으로 증명했습니다.

Sylvie Boldo, "부동 소수점 평균을 계산하는 프로그램의 공식 검증." 에서 공식 엔지니어링 방법에 관한 국제 회의 , PP. 17-32. Cham, Springer, 2015. ( 온라인 초안 )

(x+y)/2x/2+y/2binary64C[2967,2970]C 특정 사용 사례에 가장 적합한 성능을 제공합니다.

다음과 같은 예제 ISO-C99코드가 생성됩니다.

double average (double x, double y) 
{
    const double C = 1; /* 0x1p-967 <= C <= 0x1p970 */
    return (C <= fabs (x)) ? (x / 2 + y / 2) : ((x + y) / 2);
}

최근 후속 작업에서 S. Boldo와 공동 저자는 FMA (fused multiply-add) 연산과 잘 알려진 정밀도를 사용하여 IEEE-754 10 진수 부동 소수점 형식에 대해 최상의 결과를 얻는 방법을 보여주었습니다. 배가 빌딩 블록 (TwoSum) :

Sylvie Boldo, Florian Faissole 및 Vincent Tourneur, "정확한 10 진수 부동 소수점 수의 평균을 계산하는 공식적으로 입증 된 알고리즘" 에서 컴퓨터 산술 (ARITH 25)에 25 IEEE 심포지엄 6 월 2018 PP. 69-75. ( 초안 온라인 )


2

그것은 매우 효율적인 성능 현명한에 아주 간단한 방법이되지 않을 수 있지만 (1) 숫자의 확인 것도 이상 없는지 확인하거나 x또는 y(NO 오버 플로우)과 (2) 로 "정확한"부동 소수점을 유지 뺄셈이 사용 되더라도 가능한 추가 (및 (3)) 값은 음수로 저장되지 않습니다.

float difference = max(x, y) - min(x, y);
return min(x, y) + (difference / 2.0);

당신이 경우에 실제로, 정말 정확성을 위해 가고 싶어, 당신도 그 자리에서 분할을 수행 할 필요가 없습니다; 단지의 값을 반환 min(x, y)하고 difference논리적으로 단순화 이상 조작 할 수 있습니다.


내가 지금 알아 내려고하는 것은 모든 변수를 가장 큰 수보다 낮게 유지하고 정확도를 유지하기 위해 하나의 나누기 연산을 사용하면서 두 개 이상의 항목에 대해 동일한 대답을 작동 시키는 방법입니다.
IQAndreas

@ Becko Yup, 적어도 두 번 나누기를 할 것입니다. 또한 당신이 제시 한 예는 대답이 잘못되게 만들 것입니다. 의 평균과 2,4,9같다고 상상해보십시오 3,9.
IQAndreas

네 말이 맞아 내 재귀가 틀렸다 정확성을 잃지 않고 지금 수정하는 방법을 잘 모르겠습니다.
becko

이것이 가장 정확한 결과를 제공한다는 것을 증명할 수 있습니까? 즉, 경우 xy귀하의 계산에 부동 소수점에 가장 가까운을 생산, 부동 소수점된다 (x+y)/2?
becko

1
x, y가 표현할 수있는 가장 작은 숫자 일 때이 오버플로가 발생하지 않습니까?
돈 해치

1

더 높은 정밀도로 변환하고 거기에 값을 추가 한 후 다시 변환하십시오.

더 높은 정밀도에는 오버플로가 없어야하며 둘 다 유효한 부동 소수점 범위에 있으면 계산 된 숫자도 내부에 있어야합니다.

그리고 그것들이 정확하지 않으면 최악의 경우 큰 숫자의 절반에 해당합니다.


이것이 무차별 대입 방식입니다. 아마도 효과가 있지만 중간 정도의 높은 정밀도가 필요하지 않은 분석을 찾고있었습니다. 또한 중간 정도의 높은 정밀도가 필요한 정도를 추정 할 수 있습니까? 어쨌든이 답변을 삭제하지 마십시오 (+1). 나는 단지 그것을 대답으로 받아들이지 않을 것입니다.
becko

1

이론적으로 x/2가수에서 1을 빼서 계산할 수 있습니다.

그러나 실제로 이와 같은 비트 연산을 구현하는 것이 반드시 간단한 것은 아닙니다. 특히 부동 소수점 숫자의 형식을 모르는 경우 특히 그렇습니다.

이 작업을 수행 할 수 있으면 전체 작업이 3 더하기 / 빼기로 감소되어 크게 개선 될 것입니다.


0

@Roland Heath와 같은 줄을 생각하고 있었지만 아직 언급 할 수는 없습니다.

x/2지수 에서 1을 빼서 계산할 수 있습니다 (가수가 아니라 가수에서 1을 빼면 2^(value_of_exponent-length_of_mantissa)전체 값에서 빼기 ).

일반적인 경우를 제한하지 않고를 가정 해 봅시다 x < y. (경우 x > y, 변수 레이블을 재.하면 x = y, (x+y) / 2간단하다.)

  • 변형 (x+y) / 2으로 x/2 + y/2, (지수에서 하나) 개의 정수 뺄셈에 의해 수행 될 수있는
    • 그러나 표현에 따라 지수에 하한이 있습니다. 1을 빼기 전에 지수가 이미 최소 인 경우이 방법은 특별한 경우 처리가 필요합니다. 최소 지수 는 표현할 x수있는 것 x/2보다 작게 만들 것입니다 (유니티가 암시 적 선행 1로 표시된다고 가정).
    • 대신 지수에서 1을 감산 x, 쉬프트 x(있는 경우, 상기 암시 적 선두 1을 추가)을 순차적으로 우측으로의 가수.
    • 최소값이 아닌 경우 y의 지수에서 1을 뺍니다. 최소값 (y는 가수로 인해 x보다 큼) 인 경우, 가수를 오른쪽으로 하나씩 이동합니다 (있는 경우 암시 적 선행 1 추가).
    • x의 지수에 따라 새 가수를 오른쪽으로 이동합니다 y.
    • 가수가 x완전히 이동 되지 않은 경우 가수에 정수를 더합니다 . 두 지수가 모두 최소 인 경우 선행하는 오버플로가 발생합니다. 오버플로는 암시 적 선행 리더가되기 때문입니다.
  • 하나의 부동 소수점 추가.
    • 특별한 경우는 생각할 수 없습니다. 반올림을 제외하고는 위에서 설명한 이동에도 적용됩니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.