함수의 인라인 버전은 비 인라인 버전과 다른 값을 반환합니다.


85

하나는 인라인이고 다른 하나는 아닌 동일한 함수의 두 버전이 어떻게 다른 값을 반환 할 수 있습니까? 오늘 작성한 코드가 있는데 어떻게 작동하는지 잘 모르겠습니다.

#include <cmath>
#include <iostream>

bool is_cube(double r)
{
    return floor(cbrt(r)) == cbrt(r);
}

bool inline is_cube_inline(double r)
{
    return floor(cbrt(r)) == cbrt(r);
}

int main()
{
    std::cout << (floor(cbrt(27.0)) == cbrt(27.0)) << std::endl;
    std::cout << (is_cube(27.0)) << std::endl;
    std::cout << (is_cube_inline(27.0)) << std::endl;
}

모든 출력이 같을 것으로 예상 1하지만 실제로 다음을 출력합니다 (g ++ 8.3.1, 플래그 없음).

1
0
1

대신에

1
1
1

편집 : clang ++ 7.0.0은 다음을 출력합니다.

0
0
0

그리고 g ++ -Ofast this :

1
1
1

3
사용중인 컴파일러, 컴파일러 옵션 및 시스템을 알려주시겠습니까? Windows의 GCC 7.1에서 잘 작동합니다.
Diodacus 2019

31
==부동 소수점 값으로 항상 약간 예측 불가능 하지 않습니까?
500-내부 서버 오류


2
-Ofast이러한 최적화를 허용 하는 옵션 을 설정 했습니까 ?
cmdLP

4
컴파일러는 표준 라이브러리가를 반환하는 동안 cbrt(27.0)의 값을 0x0000000000000840반환합니다 0x0100000000000840. 복식은 쉼표 뒤의 16 번째 숫자가 다릅니다. 내 시스템 : archlinux4.20 x64 gcc8.2.1 glibc2.28 . gcc 또는 glibc가 올바른지 궁금합니다.
KamilCuk

답변:


73

설명

일부 컴파일러 (특히 GCC)는 컴파일 타임에 표현식을 평가할 때 더 높은 정밀도를 사용합니다. 식이 상수 입력 및 리터럴에만 의존하는 경우식이 constexpr 변수에 할당되지 않은 경우에도 컴파일 타임에 평가 될 수 있습니다. 이것이 발생하는지 여부는 다음에 따라 다릅니다.

  • 표현의 복잡성
  • 컴파일러가 컴파일 시간 평가를 수행 할 때 컷오프로 사용하는 임계 값
  • 특수한 경우에 사용되는 기타 휴리스틱 (예 : clang이 루프를 제거하는 경우)

첫 번째 경우와 같이 표현식이 명시 적으로 제공되면 복잡성이 낮아지고 컴파일러는 컴파일 타임에이를 평가할 가능성이 높습니다.

마찬가지로 함수가 인라인으로 표시된 경우 컴파일러는 인라인 함수가 평가가 발생할 수있는 임계 값을 올리기 때문에 컴파일 시간에이를 평가할 가능성이 더 높습니다.

더 높은 최적화 수준은 또한 더 높은 정밀도의 컴파일 시간 평가로 인해 gcc에서 모든 표현식이 true로 평가되는 -Ofast 예제에서와 같이이 임계 값을 증가시킵니다.

컴파일러 탐색기에서이 동작을 관찰 할 수 있습니다. -O1로 컴파일하면 인라인으로 표시된 함수 만 컴파일 타임에 평가되지만 -O3에서는 두 함수가 모두 컴파일 타임에 평가됩니다.

NB : 컴파일러 탐색기 예제에서는 printf주 함수의 복잡성을 줄여 효과를 더 잘 보이게 만들기 때문에 대신 iostream을 사용 합니다.

inline런타임 평가에 영향을 미치지 않는 입증

표준 입력에서 값을 가져 와서 컴파일 타임에 표현식이 평가되지 않도록 할 수 있으며, 이렇게하면 다음과 같이 3 개의 표현식 모두 false를 반환합니다. https://ideone.com/QZbv6X

#include <cmath>
#include <iostream>

bool is_cube(double r)
{
    return floor(cbrt(r)) == cbrt(r);
}
 
bool inline is_cube_inline(double r)
{
    return floor(cbrt(r)) == cbrt(r);
}

int main()
{
    double value;
    std::cin >> value;
    std::cout << (floor(cbrt(value)) == cbrt(value)) << std::endl; // false
    std::cout << (is_cube(value)) << std::endl; // false
    std::cout << (is_cube_inline(value)) << std::endl; // false
}

동일한 컴파일러 설정을 사용하지만 컴파일 타임에 값을 제공하여 더 높은 정밀도의 컴파일 타임 평가를 수행 하는 이 예제 와 대조됩니다 .


22

관찰 된 바와 같이, ==연산자를 사용하여 부동 소수점 값을 비교하면 다른 컴파일러와 다른 최적화 수준에서 다른 출력이 생성되었습니다.

부동 소수점 값을 비교하는 한 가지 좋은 방법 은 문서에 설명 된 상대 허용 오차 테스트입니다. 부동 소수점 허용 오차 재검토 .

먼저 다음과 같은 Epsilon( 상대 허용 오차 ) 값을 계산합니다 .

double Epsilon = std::max(std::cbrt(r), std::floor(std::cbrt(r))) * std::numeric_limits<double>::epsilon();

그런 다음 다음과 같은 방식으로 인라인 및 비 인라인 함수 모두에서 사용하십시오.

return (std::fabs(std::floor(std::cbrt(r)) - std::cbrt(r)) < Epsilon);

이제 기능은 다음과 같습니다.

bool is_cube(double r)
{
    double Epsilon = std::max(std::cbrt(r), std::floor(std::cbrt(r))) * std::numeric_limits<double>::epsilon();    
    return (std::fabs(std::floor(std::cbrt(r)) - std::cbrt(r)) < Epsilon);
}

bool inline is_cube_inline(double r)
{
    double Epsilon = std::max(std::cbrt(r), std::floor(std::cbrt(r))) * std::numeric_limits<double>::epsilon();
    return (std::fabs(std::round(std::cbrt(r)) - std::cbrt(r)) < Epsilon);
}

이제 출력은 [1 1 1]다른 컴파일러와 다른 최적화 수준에서 예상대로 ( )됩니다.

라이브 데모


max()전화 의 목적은 무엇입니까 ? 정의에 따라 floor(x)는보다 작거나 같으 x므로 max(x, floor(x))항상 같음 x.
켄 Thomases

@KenThomases : 하나 개의 인수가이 특별한 경우에서 max단지입니다 floor다른의가 필요하지 않습니다. 그러나 저는 인수 max가 서로 독립적 인 값이나 표현이 될 수 있는 일반적인 경우를 고려했습니다 .
PW

operator==(double, double)정확히 그렇게 해야하지 않습니까? 스케일링 된 엡실론보다 차이가 작은 지 확인하십시오. SO에 대한 부동 소수점 관련 질문의 약 90 %는 존재하지 않을 것입니다.
피터 - 분석 재개 모니카

사용자가 Epsilon특정 요구 사항에 따라 값 을 지정하면 더 좋습니다 .
PW
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.