c - 해결 - 두 배로 캐스팅 할 때 부동 소수점 분할을 할 때 정확도가 향상 되었습니까?




부동소수점 코드 (2)

다음 두 가지의 차이점은 무엇입니까?

float f1 = some_number;
float f2 = some_near_zero_number;
float result;

result = f1 / f2;

과:

float f1 = some_number;
float f2 = some_near_zero_number;
float result;

result = (double)f1 / (double)f2;

나는 특히 플로트에서 작동 할 때 + 무한대를 생성 할 수있는 매우 작은 f2 값에 관심이 있습니다. 얻게 될 정확성이 있습니까?

이런 종류의 캐스트를 사용하기위한 실용적인 가이드 라인도 좋을 것입니다.


"두 배로 캐스팅 할 때의 정확도 증가 및 플로트 분할시의 백분율 증가"
결과는 게시 된 두 가지 방법 외에 다른 요인에 따라 다릅니다.

C는 FLT_EVAL_METHOD 에 따라 float 레벨 연산이 여러 레벨에서 수행 FLT_EVAL_METHOD 합니다. (아래 표 참조) 현재 설정이 1 또는 2 인 경우 OP에서 게시 한 두 가지 방법으로 동일한 대답을 제공합니다.

다른 코드 및 컴파일러 최적화 수준에 따라 몫 result 는 OP의 경우 중 하나에서 후속 계산에서 더 넓은 정밀도로 사용될 수 있습니다.

이 때문에 극단적 인 float 값으로 인해 오버 플로우되거나 0.0 (정밀도가 완전히 상실된 결과)가되는 float 나눗셈과 후속 계산을 위해 최적화 된 경우에는 실수가 double 이월되었으므로 실제로 오버 / 언더 플로우가되지 않을 수 있습니다 .

잠재적 인 최적화가 이루어질 때 몫을 미래의 계산을위한 float 이되도록 강요하기 위해 코드는 volatile

volatile float result = f1 / f2;

C는 수학 연산의 정밀도를 지정하지 않지만 IEEE 754 와 같은 표준의 일반적인 적용은 binary32 나누기와 같은 단일 연산을 제공하므로 가장 가까운 답을 나타낼 수 있습니다. 분할이 double 또는 long double 과 같은 더 넓은 형식으로 발생하면 float 되돌아가는 더 넓은 지수 변환은 드물게 직접 float/float 와는 다른 해답이되는 또 다른 반올림 단계를 겪습니다.

FLT_EVAL_METHOD
-1 불확정;
0 은 모든 연산과 상수를 유형의 범위와 정밀도로 평가합니다.
1float 유형의 연산과 상수를 double 유형의 범위와 정밀도로 평가하고 long double 연산과 상수를 long double 유형의 범위와 정밀도로 평가합니다.
2 는 모든 연산과 상수를 long double 유형의 범위와 정밀도로 평가합니다.

실용 가이드 라인 :
필요한 경우 floatdouble 을 사용하여 공간을 절약하십시오. ( float 은 보통 더 좁고, 거의 같은 것, double 과 같음) 정밀도가 중요하면 double (또는 long double )을 사용하십시오.

플랫폼의 네이티브 조작이 모두 double 되기 (위해) 때문에, floatdouble 를 사용하면 속도를 향상시킬 수 있습니다 . 찾으려는 속도가 더 빠르거나 같거나 느릴 수도 있습니다. C의 대부분은 원래 float 변환에 대한 double / float 을 제외하고 레벨 FP 만 수행 되었기 때문에 원래 double 로 설계되었습니다. 나중에 C는 sinf() 와 같은 함수를 추가하여보다 빠르고 직접적인 float 연산을 지원합니다. 따라서 현대적인 컴파일러 / 플랫폼의 경우 float 가 더 빨라질 수 있습니다. 다시 : 프로파일.


개별 부동 소수점 더하기, 빼기, 곱하기 또는 나눗셈의 결과가 즉시 float 저장되면 중간 값에 대해 double 을 사용하여 정확도가 향상되지 않습니다. 그러나 연산이 함께 연결된 경우에는 정확도가 중간 정밀도가 더 높은 유형을 사용하여 향상 될 수 있습니다 . 터보 파스칼 1986 년경 코드 :

Function TriangleArea(A: Single, B:Single, C:Single): Single
Begin
  Var S: Extended;  (* S stands for Semi-perimeter *)
  S := (A+B+C) * 0.5;
  TriangleArea := Sqrt((S-A)*(S-B)*(S-C)*S)
End;

부동 소수점 연산의 모든 피연산자를 확장 (80 비트 부동 소수점 형) 유형으로 확장 한 다음 해당 유형의 변수에 저장할 때 단 정밀도 또는 배정 밀도로 다시 변환합니다. 수치 처리를위한 아주 훌륭한 의미론. 그 지역의 Turbo C는 비슷하게 행동했지만 오히려 도움이되지 않았지만 중간 결과를 보유 할 수있는 숫자 유형을 제공하지 못했습니다. 중간 결과를 보유 할 수있는 가변 유형을 제공하는 언어의 실패로 인해 언어가 언어를 올바르게 지원하지 못하는 실제 문제가 발생했을 때보다 정밀도가 높은 중간 결과 유형의 개념을 부당하게 비판하게되었습니다.

어쨌든, 만약 위의 메소드를 C #과 같은 현대적인 언어로 작성한다면 :

    public static float triangleArea(float a, float b, float c)
    {
        double s = (a + b + c) * 0.5;
        return (double)(Math.Sqrt((s - a) * (s - b) * (s - c) * s));
    }

컴파일러가 계산을 수행하기 전에 덧셈의 피연산자를 double 늘리면 코드가 잘 작동하지만 그럴 수도 있고하지 않을 수도 있습니다. 컴파일러가 float 계산을 수행하면 정밀도가 무시 무시할 수 있습니다. 위의 수식을 사용하여 긴면이 16777215이고 짧은면이 4 인 이등변 삼각형의 면적을 계산할 때, 예를 들어 열심히 승격하면 3.355443E + 7의 정확한 결과가 산출되지만 float 수학을 수행하는 경우 피연산자 순서는 5.033165E + 7 [50 %가 너무 큼] 또는 16777214.0 [50 %가 너무 작음]입니다.

위의 코드가 일부 환경에서는 완벽하게 작동하지만 다른 컴파일러에서는 완전히 위조 된 결과를 산출하더라도 컴파일러는 일반적으로 상황에 대해 경고하지 않습니다.

float 즉시 저장 될 개별 연산은 float 유형과 정확하게 동일하게 수행 될 수 있지만 유형이 double 일 수 있으므로 작업을 결합 할 때 피연산자를 열심히 홍보하는 것이 종종 도움이됩니다. 경우에 따라 재정렬 작업으로 인해 프로모션 상실로 인해 발생하는 문제를 피할 수 있습니다 (예 : 위의 수식에는 5 개의 덧셈, 4 개의 곱셈 및 제곱근이 사용되며 수식은 다음과 같이 다시 작성됩니다.

Math.Sqrt((a+b+c)*(b-a+c)*(a-b+c)*(a-c+b))*0.25

추가 횟수를 8로 늘리지 만 단 정밀도로 수행하더라도 올바르게 작동합니다.







ieee-754