C ++ 경고 : double을 0으로 나누기


98

사례 1 :

#include <iostream>

int main()
{
    double d = 15.50;
    std::cout<<(d/0.0)<<std::endl;
}

경고없이 컴파일되고 인쇄 inf됩니다. 좋아요, C ++는 0으로 나누기를 처리 할 수 ​​있습니다 ( 라이브 참조 ).

그러나,

사례 2 :

#include <iostream>

int main()
{
    double d = 15.50;
    std::cout<<(d/0)<<std::endl;
}

컴파일러는 다음 경고를 표시합니다 (라이브 참조 ).

warning: division by zero [-Wdiv-by-zero]
     std::cout<<(d/0)<<std::endl;

두 번째 경우 컴파일러가 경고를 표시하는 이유는 무엇입니까?

인가 0 != 0.0?

편집하다:

#include <iostream>

int main()
{
    if(0 == 0.0)
        std::cout<<"Same"<<std::endl;
    else
        std::cout<<"Not same"<<std::endl;
}

산출:

Same

9
두 번째 경우에는 정수로 0을 취하고 계산이 나중에 double을 사용하여 수행 되더라도 경고를 삭제한다고 가정합니다 (d가 double 일 때의 동작이어야한다고 생각합니다).
Qubit

10
정말 QoI 문제입니다. 경고 또는 경고 없음은 C ++ 표준 자체에서 요구하는 사항이 아닙니다. GCC를 사용하고 있습니까?
이야기꾼 - Unslander 모니카


5
@StoryTeller QoI 란 무엇입니까? en.wikipedia.org/wiki/QoI ?
user202729 jul.

5
마지막 질문과 관련하여 "0이 0.0과 같습니까?" 답은 이 동일하다는 것입니다.하지만 아시다시피 그것이 동일하다는 것을 의미하지는 않습니다. 다른 유형! 'A'가 65와 동일하지 않은 것처럼.
Mr Lister

답변:


108

0으로의 부동 소수점 나누기는 IEEE에 의해 잘 정의되고 무한대를 제공합니다 (분자 값에 따라 양수 또는 음수 (또는 NaN± 0) ).

정수의 경우 무한대를 나타낼 수있는 방법이 없으며 언어는 정의 되지 않은 동작을 가지 도록 연산을 정의 하므로 컴파일러가 해당 경로에서 벗어나도록 도와줍니다.

그러나이 경우 분자가이므로 double제수 ( 0)도 double로 승격되어야하며 경고를 제공하지 않는 동안 경고를 줄 이유가 없으므로 이것이 0.0컴파일러 버그라고 생각합니다.


8
둘 다 부동 소수점 분할입니다. 에서 d/0, 0의 형식으로 변환됩니다 d.

43
C ++은 (내가 다른 표준을 사용하여 컴파일러를 본 적이에도 불구하고) IEEE 754를 사용하는 것이 필요하지 않습니다 ..
Yksisarvinen

1
@hvd, 좋은 점, 그 경우 컴파일러의 버그 같은이 모습
은 Motti

14
두 경우 모두 경고하거나 두 경우 모두 경고하지 않는 데 동의합니다 (컴파일러가 부동 나누기를 0으로 처리하는 방식에 따라 다름)
MM

8
부동 소수점을 0으로 나누는 것도 UB라는 것이 확실합니다. GCC가 IEEE 754에 따라 구현한다는 것입니다. 그래도 그렇게 할 필요는 없습니다.
마틴 보너 모니카 지원

42

표준 C ++에서 두 경우 모두 정의되지 않은 동작 입니다. 하드 드라이브 포맷을 포함하여 모든 일이 발생할 수 있습니다. "return inf. Ok"또는 다른 행동을 기대하거나 의존해서는 안됩니다.

컴파일러는 분명히 한 경우에는 경고를 제공하고 다른 경우에는 경고를 제공하지 않기로 결정했지만 이것이 하나의 코드는 정상이고 다른 하나는 그렇지 않다는 의미는 아닙니다. 컴파일러의 경고 생성의 특징 일뿐입니다.

C ++ 17 표준 [expr.mul] / 4에서 :

이항 /연산자는 몫을 산출하고 이항 %연산자는 첫 번째 표현식을 두 번째로 나눈 나머지를 산출합니다. 또는 의 두 번째 피연산자 가 0이면 동작이 정의되지 않습니다./%


21
사실이 아닙니다. 부동 소수점 산술에서 0으로 나누는 것이 잘 정의되어 있습니다.
Motti

9
@Motti-C ++ 표준만으로 제한한다면 그러한 보장은 없습니다. 솔직히 말해서이 질문의 범위는 잘 지정되어 있지 않습니다.
이야기꾼 - Unslander 모니카

9
경우에 것을 (나는이에 대한 표준 문서 자체에보고하지 않은하지만) 나는 확신 @StoryTeller std::numeric_limits<T>::is_iec559이다 true제로에 의해 다음, 부문 TUB (그리고하지 그것의 대부분의 플랫폼에 true대한 doublefloat이식을하더라도 당신은거야, if또는 if constexpr) 를 사용하여 명시 적으로 확인해야합니다 .
Daniel H

6
@DanielH- "합리적"은 실제로 매우 주관적입니다. 이 질문이 언어 변호사 라고 태그가 붙은 경우 에는 완전히 다른 (훨씬 더 작은) 합리적인 가정이있을 것입니다.
이야기꾼 - Unslander 모니카

5
@MM 당신이 말한 것과 정확히 일치하지만 더 이상은 없습니다. 정의되지 않은 것은 요구 사항이 부과되지 않음을 의미하지 않으며 표준의해 부과 되는 요구 사항이 없음을 의미 합니다 . 이 경우 구현 이 다음 is_iec559과 같이 정의 true한다는 사실은 구현 이 표준이 정의되지 않은 동작을 문서화 하고 있음을 의미합니다 . 이는 구현의 문서를 프로그래밍 방식으로 읽을 수있는 경우입니다. 단 하나도 아닙니다 is_modulo. 부호있는 정수 유형 에도 동일하게 적용됩니다 .

12

답변을 내 추측 이 특정 문제는 경고가 컴파일러가 방출 될 전에 의 변환을 수행 int하는 double.

따라서 단계는 다음과 같습니다.

  1. 식 구문 분석
  2. 산술 연산자 /(T, T2) , 여기서 T=double, T2=int.
  3. 그 확인 std::is_integral<T2>::value입니다 trueb == 0이 트리거 경고 -을.
  4. 경고 방출
  5. 의 암시 적 변환 수행 T2에를double
  6. 잘 정의 된 분할을 수행합니다 (컴파일러가 IEEE 754를 사용하기로 결정했기 때문에).

이것은 물론 추측이며 컴파일러 정의 사양을 기반으로합니다. 표준 관점에서 우리는 가능한 정의되지 않은 동작을 다루고 있습니다.


이는 GCC 문서 에 따라 예상되는 동작입니다
(btw.이 플래그는 GCC 8.1에서 명시 적으로 사용할 수없는 것 같습니다).

-Wdiv-by-zero
컴파일시 정수를 0으로 나누는 것에 대해 경고합니다. 이것이 기본값입니다. 경고 메시지를 금지하려면 -Wno-div-by-zero를 사용하십시오. 부동 소수점을 0으로 나누는 것은 무한 성과 NaN을 얻는 합법적 인 방법 일 수 있으므로 경고하지 않습니다.


2
이것은 C ++ 컴파일러가 작동하는 방식이 아닙니다. 컴파일러는 /분할임을 알기 위해 오버로드 해결을 수행 해야합니다. 왼쪽이 Foo객체 였고가있을 operator/(Foo, int)경우 나눗셈이 아닐 수도 있습니다. 컴파일러 built-in / (double, double)는 오른쪽의 암시 적 변환을 사용하여 선택한 경우에만 나눗셈을 알고 있습니다 . 그러나 수단 것을 부문에 의해 수행되지 않습니다 int(0)그것으로 부서를하고있어, double(0).
MSalters

@MSalters 이것을 참조하십시오. C ++에 대한 내 지식은 제한적이지만 참조에 따르면 operator /(double, int)확실히 허용됩니다. 그런 다음 다른 작업보다 먼저 변환이 수행되지만 GCC T2는 정수 유형 인 경우 빠른 확인 을 수행 b == 0하고 그렇다면 경고를 표시 할 수 있습니다. 이것이 완전히 표준을 준수하는지 확실하지 않지만 컴파일러는 경고를 정의하고 언제 트리거해야 하는지를 완전히 자유롭게 정의 할 수 있습니다.
Yksisarvinen

2
여기서는 내장 연산자에 대해 이야기하고 있습니다. 재밌 네요. 실제로 함수가 아니므로 주소를 가져올 수 없습니다. 따라서 operator/(double,int)실제로 존재 하는지 확인할 수 없습니다 . 예를 들어 컴파일러는 a/b상수 ba * (1/b). 물론 이는 더 이상 operator/(double,double)런타임에 호출하지 않고 operator*(double,double). 그러나 이제는 넘어가는 최적화 프로그램 1/0, 공급해야하는 상수operator*
MSalters

일반적으로 포인트 부문 부동 @MSalters은 2로, 아마 떨어져 예외적 인 경우에서 곱셈으로 대체 할 수없는
user202729

2
@ user202729 : GCC는 정수 나누기도합니다. 잠깐 동안 그대로 두십시오. GCC는 정수 나누기를 정수 곱셈으로 대체합니다. 예, 가능합니다. GCC는 링에서 작동하고 있다는 것을 알고 있기 때문입니다 (모듈로 2 ^ N의 숫자)
MSalters

9

나는이 답변에서 UB / UB debacle에 들어 가지 않을 것입니다.

난 그냥 그 시점 싶어 0하고 0.0 다른 에도 불구하고 0 == 0.0true로 평가. 0int문자와 0.0A는 double문자.

그러나이 경우 최종 결과는 동일합니다. d/0이는 ddouble이므로 0암시 적으로 double로 변환 되기 때문에 부동 소수점 나누기 입니다.


5
나는 이것이 일반적인 산술 변환이 나누어 지정 주어진, 관련이 표시되지 않습니다 double에 의해 int수단하면 해당 int로 변환되고 double , 그것은 표준에 지정되어 있는지 0에 변환 0.0 (conv.fpint / 2)
MM

@MM 영업 이익이 있는지 알고 싶어 0과 동일0.0
bolov

2
질문은 "입니까 0 != 0.0?"입니다. OP는 "동일한"것인지 묻지 않습니다. 또한 질문의 의도 d/0는 다르게 행동 할 수 있는지 여부와 관련이있는 것 같습니다d/0.0
MM

2
@MM- OP가 물었습니다 . 그들은 그 상수 편집으로 괜찮은 네티켓을 실제로 보여주지 않습니다.
이야기꾼 - Unslander 모니카

7

나는 그것을 주장 foo/0하고 foo/0.0있다 되지 같은. 즉, 첫 번째 (정수 나눗셈 또는 부동 소수점 나눗셈)의 결과 효과는의 유형에 크게 의존하는 foo반면 두 번째 경우에는 동일하지 않습니다 (항상 부동 소수점 나눗셈이됩니다).

둘 중 하나가 UB인지 여부는 관련이 없습니다. 표준 인용 :

허용되지 않는 정의되지 않은 동작은 예측할 수없는 결과로 상황을 완전히 무시하는 것, 번역 또는 프로그램 실행 중에 환경 특성 (진단 메시지 발행 여부에 관계없이)의 문서화 된 방식으로 동작 하는 것, 번역 또는 실행 종료 ( 발행과 함께)에 이르기까지 다양 합니다. 진단 메시지).

(강조 광산)

" 진실 값으로 사용되는 할당 주위에 괄호 제안 "경고를 고려하십시오 . 컴파일러에게 할당 결과 를 실제로 사용하고 싶다고 알리는 방법 은 명시적이고 할당 주위에 괄호를 추가하는 것입니다. 결과 명령문은 동일한 효과를 가지지 만 수행중인 작업을 컴파일러에 알립니다. 같은 대해 말할 수있다 foo/0.0: 당신이 명시 적으로 사용하여 "이 점 사업부를 떠"컴파일러를 이야기하고 있기 때문에 0.0대신에 0당신을, 컴파일러 신탁 경고를 발행하지 않습니다.


1
둘 다 일반 산술 변환거쳐서 공통 유형으로 가져와야하며 두 경우 모두 부동 소수점 분할이 남습니다.
Shafik Yaghmour

@ShafikYaghmour 대답의 요점을 놓쳤습니다. 의 유형이 무엇인지 언급 한 적이 없습니다 foo. 그것은 의도적 인 것입니다. 귀하의 주장은 foo부동 소수점 유형 인 경우에만 참입니다 .
Cássio Renan

나는 그렇지 않았다. 컴파일러는 타입 정보를 가지고 있고 변환을 이해한다. 아마도 텍스트 기반의 정적 분석기는 그런 것들에 의해 포착 될 수 있지만 컴파일러는 그렇게해서는 안된다.
Shafik Yaghmour

내 요점은 예, 컴파일러는 일반적인 산술 변환을 알고 있지만 프로그래머가 명시적일 때 경고를 발행하지 않기로 선택한다는 것입니다. 요점은 이것이 버그가 아니라 의도적 인 행동이라는 것입니다.
Cássio Renan

그런 다음 두 경우 모두 부동 소수점 분할이기 때문에 내가 지적한 문서 가 올바르지 않습니다. 따라서 문서가 잘못되었거나 진단에 버그가 있습니다.
Shafik Yaghmour

4

gcc의 버그 등이 외모에 대한 문서 -Wno-div-by-zero 분명히 말한다 :

컴파일시 정수를 0으로 나누는 것에 대해 경고하지 마십시오. 부동 소수점을 0으로 나누는 것은 무한 성과 NaN을 얻는 합법적 인 방법 일 수 있으므로 경고하지 않습니다 .

그리고 [expr.arith.conv] 에서 다루는 일반적인 산술 변환 후에 두 피연산자는 double이됩니다 .

산술 또는 열거 유형의 피연산자를 예상하는 많은 이항 연산자는 유사한 방식으로 변환을 일으키고 결과 유형을 산출합니다. 목적은 결과의 유형이기도 한 공통 유형을 생성하는 것입니다. 이 패턴을 일반적인 산술 변환이라고하며 다음과 같이 정의됩니다.

...

그렇지 않으면 피연산자가 double이면 다른 피연산자가 double로 변환됩니다.

[expr.mul] :

* 및 /의 피연산자는 산술 또는 범위가 지정되지 않은 열거 유형을 가져야합니다. %의 피연산자는 정수 또는 범위가 지정되지 않은 열거 유형을 가져야합니다. 일반적인 산술 변환은 피연산자에서 수행되며 결과 유형을 결정합니다.

부동 소수점을 0으로 나누는 것이 정의되지 않은 동작인지 여부와 다른 구현 방식이 여기에 내 대답 처럼 보입니다 . TL; DR; gcc 는 부동 소수점을 0으로 나누기 위해 Annex F wrt를 따르는 것처럼 보이 므로 undefined는 여기서 역할을 수행하지 않습니다. clang에 대한 대답은 다를 것입니다.


2

부동 소수점을 0으로 나누는 것은 정수를 0으로 나누는 것과 다르게 동작합니다.

IEEE 부동 소수점 + INF와 -INF 사이의 표준 차별화는, 정수는 무한대를 저장할 수있다. 0으로 정수 나누기 결과는 정의되지 않은 동작입니다. 부동 소수점을 0으로 나누는 것은 부동 소수점 표준에 의해 정의되며 + inf 또는 -inf가됩니다.


2
이것은 사실이지만 부동 소수점 분할이 두 경우 모두에서 수행 되기 때문에 이것이 질문과 어떻게 관련되어 있는지 명확하지 않습니다 . OP 코드에는 정수 나눗셈이 없습니다.
Konrad Rudolph
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.