source

조건부 부정 대신 abs() 또는 fabs()를 사용하는 이유는 무엇입니까?

goodcode 2022. 7. 31. 23:00
반응형

조건부 부정 대신 abs() 또는 fabs()를 사용하는 이유는 무엇입니까?

왜C/C++, ",abs() ★★★★★★★★★★★★★★★★★」fabs()다음 코드를 사용하지 않고 변수의 절대값을 찾을 수 있습니까?

int absoluteValue = value < 0 ? -value : value;

더 낮은 수준의 지시사항이 적은 것과 관련이 있나요?

이 제안하는 '복근'은'조건부 복근'과 하지 않습니다.std::abs (오류)fabs부동 소수점 번호에 대해서는, 예를 참조해 주세요.

#include <iostream>
#include <cmath>

int main () {
    double d = -0.0;
    double a = d < 0 ? -d : d;
    std::cout << d << ' ' << a << ' ' << std::abs(d);
}

출력:

-0 -0 0

의 「」-0.0 ★★★★★★★★★★★★★★★★★」0.0는 동일한 실수 '0'을 나타냅니다. 이 차이는 결과가 어떻게 사용되는지에 따라 중요하거나 중요하지 않을 수 있습니다.0으로 하며, abs는 됩니다.-0.0저는 개인적으로 어떤 "절대값"을 계산하기 위해 사용되는 모든 것이 이 행동과 일치해야 한다고 생각합니다.

정수의 경우 두 변형 모두 실행 시 및 동작에서 동일합니다.(실시간 예)

지.........std::abs(또는 적합 C 등가물)은 정확하고 읽기 쉽다고 알려져 있으므로 항상 이러한 것을 선호해야 합니다.

가장 먼저 떠오르는 것은 가독성입니다.

다음 두 줄의 코드를 비교합니다.

int x = something, y = something, z = something;
// Compare
int absall = (x > 0 ? x : -x) + (y > 0 ? y : -y) + (z > 0 ? z : -z);
int absall = abs(x) + abs(y) + abs(z);

컴파일러는 하위 계층에서도 같은 작업을 수행할 수 있습니다.최소한 최신의 컴파일러를 사용할 수 있습니다.

그러나 적어도 부동 소수점에서는 무한대, NaN(not-a-number), 마이너스 0 등의 모든 특수한 경우를 처리하려면 수십 개의 줄을 쓰게 됩니다.

읽기만 아니라 읽기도 쉬워요.abs경우 을 취하는 입니다.보다 작으면 부정한다고 읽는 것보다 절대값을 취하는 것이다.

인 는, 「stupid」라고 하는 코드에 대해서는, 보다 .a = (a < 0)?-a:a으로 「」를 붙이기 if) 그 수 ).

Clang(6.0 프리 릴리즈)과 gcc(4.9.2)는 모두 두 번째 케이스에 대해 더 나쁜 코드를 생성합니다.

제가 쓴 샘플은 다음과 같습니다.

#include <cmath>
#include <cstdlib>

extern int intval;
extern float floatval;

void func1()
{
    int a = std::abs(intval);
    float f = std::abs(floatval);
    intval = a;
    floatval = f;
}


void func2()
{
    int a = intval < 0?-intval:intval;
    float f = floatval < 0?-floatval:floatval;
    intval = a;
    floatval = f;
}

clang은 func1을 위해 다음 코드를 만듭니다.

_Z5func1v:                              # @_Z5func1v
    movl    intval(%rip), %eax
    movl    %eax, %ecx
    negl    %ecx
    cmovll  %eax, %ecx
    movss   floatval(%rip), %xmm0   # xmm0 = mem[0],zero,zero,zero
    andps   .LCPI0_0(%rip), %xmm0
    movl    %ecx, intval(%rip)
    movss   %xmm0, floatval(%rip)
    retq

_Z5func2v:                              # @_Z5func2v
    movl    intval(%rip), %eax
    movl    %eax, %ecx
    negl    %ecx
    cmovll  %eax, %ecx
    movss   floatval(%rip), %xmm0   
    movaps  .LCPI1_0(%rip), %xmm1 
    xorps   %xmm0, %xmm1
    xorps   %xmm2, %xmm2
    movaps  %xmm0, %xmm3
    cmpltss %xmm2, %xmm3
    movaps  %xmm3, %xmm2
    andnps  %xmm0, %xmm2
    andps   %xmm1, %xmm3
    orps    %xmm2, %xmm3
    movl    %ecx, intval(%rip)
    movss   %xmm3, floatval(%rip)
    retq

g++ func1:

_Z5func1v:
    movss   .LC0(%rip), %xmm1
    movl    intval(%rip), %eax
    movss   floatval(%rip), %xmm0
    andps   %xmm1, %xmm0
    sarl    $31, %eax
    xorl    %eax, intval(%rip)
    subl    %eax, intval(%rip)
    movss   %xmm0, floatval(%rip)
    ret

g++ func2:

_Z5func2v:
    movl    intval(%rip), %eax
    movl    intval(%rip), %edx
    pxor    %xmm1, %xmm1
    movss   floatval(%rip), %xmm0
    sarl    $31, %eax
    xorl    %eax, %edx
    subl    %eax, %edx
    ucomiss %xmm0, %xmm1
    jbe .L3
    movss   .LC3(%rip), %xmm1
    xorps   %xmm1, %xmm0
.L3:
    movl    %edx, intval(%rip)
    movss   %xmm0, floatval(%rip)
    ret

두 번째 형태에서는 두 가지 케이스가 더 복잡하며, gcc의 경우 브랜치를 사용합니다.Clang은 더 많은 명령을 사용하지만 분기는 사용하지 않습니다.어떤 프로세서 모델이 더 빠른지 모르겠지만 명령을 더 많이 하는 것이 더 나은 경우는 거의 없습니다.

조건부 부정 대신 abs() 또는 fabs()를 사용하는 이유는 무엇입니까?

다양한 이유가 이미 언급되었지만 조건부 코드 이점을 고려하십시오.abs(INT_MIN)피해야 합니다.


대신 조건부 코드를 사용하는 데는 충분한 이유가 있습니다.abs()정수의 의 절대값이 요구될 때

// Negative absolute value

int nabs(int value) {
  return -abs(value);  // abs(INT_MIN) is undefined behavior.
}

int nabs(int value) {
  return value < 0 ? value : -value; // well defined for all `int`
}

양의 절대 함수가 필요한 경우value == INT_MIN진짜 가능성이 있어요.abs()매우 명확하고 속도도 뛰어나지만 코너 케이스에 실패하였습니다.다양한 대안

unsigned absoluteValue = value < 0 ? (0u - value) : (0u + value);

특정 아키텍처에서 조건부 브랜치보다 더 효율적인 로우 레벨 구현이 있을 수 있습니다.예를 들어, CPU는abs명령 또는 분기 오버헤드 없이 부호 비트를 추출하는 방법.산술적 우측 시프트를 가정하면 레지스터 r이 음의 경우 -1로 채워지고 양의 경우 0으로 채워질 수 있습니다.abs x될 수 있다(x+r)^r(그리고 Mats Peterson의 답변을 보면 g++는 실제로 x86에서 이 작업을 수행합니다).

IEEE 부동소수점 상황에 대한 다른 답변이 있습니다.

라이브러리를 신뢰하지 않고 조건부 분기를 실행하도록 컴파일러에 지시하는 것은 시기상조일 수 있습니다.

...그리고 매크로로 만들 수 있다면, 원하지 않는 여러 가지 평가(측면 효과)를 받을 수 있습니다.고려사항:

#define ABS(a) ((a)<0?-(a):(a))

및 용도:

f= 5.0;
f=ABS(f=fmul(f,b));

로 확장될 것이다.

f=((f=fmul(f,b)<0?-(f=fmul(f,b)):(f=fmul(f,b)));

함수 호출은 의도하지 않은 부작용을 일으키지 않습니다.

복잡한 표현을 입력해 주세요.abs()로 코드화하면expr > 0 ? expr : -expr전체 표현을 세 번 반복해야 되고, 두 번 평가됩니다.
또한 두 결과(대장 전후)가 다른 유형(예:signed int/unsigned intreturn 스테이트먼트에서의 사용을 디세블로 합니다.물론 임시 변수를 추가할 수도 있지만, 이 경우 일부만 해결되며 어떤 방식으로도 더 나은 것은 아닙니다.

abs()의 배후에 있는 의도는 "(무조건) 이 숫자의 부호를 양수로 설정하는 것입니다.현재 숫자의 상태에 근거해 조건부로 실장할 필요가 있다고 해도, 보다 복잡한 「이…이…이…이…이」가 아니라, 단순한 「이」라고 생각할 수 있는 것이 도움이 될 것입니다.

컴파일러가 abs()와 조건부 부정이 모두 같은 목표를 달성하려고 시도하고 있다고 판단할 수 없다고 가정하면 조건부 부정은 비교 명령, 조건부 점프 명령 및 이동 명령으로 컴파일되는 반면 abs()는 실제 절대값 명령 집합으로 컴파일됩니다.t 그런 것, 혹은 조금 현명하고, 기호 비트를 제외하고 모든 것을 동일하게 유지합니다.위의 명령어는 보통 1사이클이기 때문에 abs()를 사용하는 것은 적어도 조건부 부정보다 빠를 수 있습니다(컴파일러는 조건부정을 사용할 때 절대값을 계산하려고 하는 것을 인식하고 절대값 명령을 생성할 수 있기 때문입니다).컴파일된 코드에 변경이 없는 경우에도 조건부정보다 abs()가 읽기 쉬워집니다.

언급URL : https://stackoverflow.com/questions/48608993/why-use-abs-or-fabs-instead-of-conditional-negation

반응형