왜 많은 (오래된) 프로그램이 round (input) 대신 floor (0.5 + input)를 사용합니까?


80

차이점은 다음 코드 와 같이 내가 믿는 타이 브레이킹에 대한 입력을 제공하는 반환 값에 있습니다 .

int main()
{
    std::cout.precision(100);

    double input = std::nextafter(0.05, 0.0) / 0.1;
    double x1 = floor(0.5 + input);
    double x2 = round(input);

    std::cout << x1 << std::endl;
    std::cout << x2 << std::endl;
}

다음을 출력합니다.

1
0

그러나 그들은 결국 다른 결과 일뿐이며 선호하는 것을 선택합니다. 내가 사용하는 "오래된"C / C ++ 프로그램을 많이 볼 floor(0.5 + input)대신을 round(input).

역사적인 이유가 있습니까? CPU에서 가장 저렴합니까?


20
std :: round는 케이스의 절반을 0에서 멀어지게합니다. 그것은 바닥 (f + .5)과 같이 수학적으로 균일하지 않습니다. 중간 케이스는 항상 위쪽을 향합니다. 바닥 방법을 사용하는 이유는 엔지니어링 세계에서 적절한 반올림을 위해 필요하기 때문입니다.
Michaël Roy

7
C ++ 이전 C ++ 11의 float에 대한 round () 에서 언급했듯이 라운드 가 없습니다. 내 대답에서 언급했듯이 자신의 라운드를 올바르게 작성하는 것은 어려운 문제입니다.
Shafik Yaghmour

16
@Arne std :: round ()를 사용하면 반올림 된 값 -0.5와 +0.5 사이의 거리는 2입니다. floor를 사용하면 1입니다. 두 값이 반대 부호를 가질 때만 발생합니다. 직선을 그리려고 할 때 매우 짜증나거나 잘못된 텍스처 픽셀을 선택하게합니다.
Michaël Roy

20
일부 프로그래밍 언어 및 환경 (.NET 포함)은 x.5가 가장 가까운 EVEN 숫자로 반올림되는 Banker 's Rounding이라는 속임수를 사용합니다. 따라서 0.5는 0으로 반올림되고 1.5는 2로 반올림됩니다. 디버깅 할 때 발생할 수있는 혼란을 상상할 수 있습니다. 이 사악한 '기능'에 대한 해결책은 .Round () 함수가 전혀없고 대신 .RoundBankers (), .RoundHalfUp (), .RoundHalfDown () 등 (또는 .BankersRound () 등)이있는 것이라고 생각합니다. 그러나 intellisense는 .RoundBankers ())에서 더 잘 작동합니다. 적어도 그렇게하면 당신은 무엇을 기대해야하는지 알 수밖에 없을 것입니다.
user3685427

8
@ user3685427 : Banker 's Rounding0에서 반올림 하여 도입 된 미묘하고 체계적인 상향 편향을 제거해야하는 재무 및 통계 애플리케이션에 필요합니다 . 하드웨어 부동 소수점 구현에 대한 실제 지식 없이는 구현하는 것이 거의 불가능하므로 C #에서 기본값으로 선택됩니다.
Pieter Geerkens 2011

답변:


115

std::roundC ++ 11에 도입되었습니다. 그 전에는 std::floor프로그래머가 사용하고 있었기 때문에 사용할 수있었습니다.


아무것도. 그러나 C ++ 11보다 오래되었습니다. 나는 C ++이 11 이전에 그것을 얻었다 고 논리적이라고 생각했다. 그게 다야;)
markzzz

12
@markzzz-C ++ 표준 라이브러리는 C의 표준 라이브러리를 자동으로 상속하지 않습니다. 신중한 선택과 선택이 진행됩니다. C99와 동기화하는 데 12 년이 걸렸습니다.
StoryTeller-Unslander Monica

2
@haccks : 그렇습니다. IMHO C는 C ++ 11까지의 수학 함수 측면에서 C ++보다 앞서있었습니다.
밧세바

3
주목해야하는 방법은 몇 가지 입력을위한 휴식을 사용 하고 C ++ 03 가지 @markzzz 지점으로 이동하는 C90에 의존하면서 또한 C ++ (11)는 C99에 의존하고있다.
Shafik Yaghmour

1
@markzzz : 당신의 발언은 (비평가에도 불구하고) 자리 잡고 있습니다. 문제는 C ++ 표준 없이는 긴 간격이 있었고 첫 번째 C ++ 표준은 C ++ 98이었고 첫 번째 주요 개정판은 C ++ 11이었습니다. 사소한 업데이트 인 C ++ 03이 있었지만 Wikipedia 페이지에서 언급했듯이 대부분 "버그 수정"릴리스였습니다. 따라서 C ++ 11은 iatus의 13 년 만에 C 표준 라이브러리를 따라 잡을 수있는 첫 번째 기회였습니다.
Matthieu M.

21

역사적 이유는 전혀 없습니다. 이런 종류의 일탈은 해마다 존재했습니다. 사람들은 그들이 아주 아주 장난 스러울 때 이것을합니다. 이것은 부동 소수점 산술의 남용이며 많은 경험 많은 전문 프로그래머가 그것에 속합니다. Java bods도 버전 1.7까지 수행했습니다. 웃긴 녀석들.

내 추측은 괜찮은 기본 독일어 반올림 기능은 C ++ 11 (C가 C99에서 C를 얻었음에도 불구하고)까지는 공식적으로 사용할 수 없었지만 실제로는 소위 대안을 채택한 변명의 여지가 없습니다.

문제는 다음과 같습니다. floor(0.5 + input) 항상 해당 std::round호출 과 동일한 결과를 복구하지는 않습니다 !

그 이유는 매우 미묘합니다. a.5정수에 대한 독일어 반올림의 컷오프 지점 a은 우주의 우연한 속성에 의해 이원 적 합리적 입니다. 이것은 IEEE754 부동 소수점에서 52의 2 제곱까지 정확하게 표현할 수 있으며 그 후 반올림은 어쨌든 std::round작동하지 않으므로 항상 올바르게 작동합니다. 다른 부동 소수점 체계에 대해서는 설명서를 참조하십시오.

그러나에 추가 0.5하면 double일부 값에 대해 약간의 언더 슈트 또는 오버 슈트를 유발하는 부정확성이 발생할 수 있습니다. 생각해 보면, 두 개의 double값을 함께 더하는 것, 즉 무의식적 인 부정 변환의 시작입니다. 그리고 입력의 매우 강력한 함수 (예 : 반올림 함수)를 적용하는 것은 눈물로 끝납니다.

하지마 .

참조 : Math.round (0.49999999999999994)가 1을 반환하는 이유는 무엇입니까?


2
의견은 확장 된 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
Andy

2
nearbyint()일반적으로는 0에서 벗어나는 펑키 타이 브레이크 대신 현재 반올림 모드를 사용 round()하기 때문에 보다 나은 선택 입니다. 둘 다 C ++ 11의 C ++에 추가되었습니다. nearbyintround()
Peter Cordes 2017

9

나는 이것이 당신이 실수하는 곳이라고 생각합니다.

그러나 그들은 결국 다른 결과 일뿐이며 선호하는 것을 선택합니다. round (input) 대신 floor (0.5 + input)를 사용하는 "오래된"C / C ++ 프로그램이 많이 있습니다.

그것은 사실이 아닙니다. 도메인에 적합한 반올림 체계를 선택해야 합니다 . 금융 애플리케이션에서는 은행가의 규칙을 사용하여 반올림합니다 (부동산을 사용하지 않음). 그러나 샘플링 할 때를 사용하여 반올림 static_cast<int>(floor(f + .5))하면 샘플링 노이즈가 줄어들고 동적 범위가 증가합니다. 픽셀을 정렬 할 때, 즉 위치를 화면 좌표로 변환 할 때 다른 반올림 방법을 사용하면 구멍, 간격 및 기타 아티팩트가 생성됩니다.


"이것은 동적 범위를 증가시킵니다"-의미없는 추가 텍스트처럼 보입니다. 실수로 다른 곳에서 복사하여 붙여 넣었습니까? 삭제하고 싶을 수도 있습니다.
아나톨리 그

아니요. 샘플링 노이즈를 줄이면 노이즈 플로어가 감소하고 실제로 동적 범위가 증가합니다.
Michaël Roy

다이내믹 레인지 증가 / 노이즈 감소를 설명하기위한 간단한 예나 참고 자료를 제공 할 수 있습니까?
Ruslan

1
표준 정수 (부족) 반올림, 0에서 마이너스 1 사이의 모든 값이 "사라짐" '또는 오히려 부호 변경 (동일한 값 및 fo 0에서 + 1 입력)을 사용하면 모든 음수 값이 최소 1 비트만큼 오프셋됩니다. 여기에 왜곡이 추가 된 약간의 노이즈가 있습니다.
Michaël Roy

4

간단한 이유는 숫자 반올림 방법이 다르기 때문에 사용 된 방법을 알지 못하면 다른 결과를 얻을 수 있기 때문입니다.

floor ()를 사용하면 결과와 일치 할 수 있습니다. float가 .5 이상이면 추가하면 다음 정수로 올라갑니다. 그러나 .49999는 소수만 떨어 뜨립니다.


+1이 답변의 요점은 질문에 대한 바로 그 의견에 의해 이루어지며, 무엇을하는지에 대한 의견이 일치 round()하지 않습니다.
Loduwijk

@Aaron floor(x + 0.5)하지만 무엇을하는지에 대한 대답은 잘못되었습니다 .
EOF

오, 하! 그럼 잘 잡아. 아이러니합니다. "우리가 Y에 대해 동의하지 않거나 완전한 지식이 없기 때문에 더 잘 알려진 X를 사용하십시오." 그렇다면 X에도 동일하게 적용될 때 무엇을 하시겠습니까?
Loduwijk

1
@Aaron Easy. nearbyint(x)부동 소수점 환경을 엉망으로 만들지 않았다면 올바른 일을하고 sane (가장 가까운 짝수까지) 반올림을 사용하는을 사용하십시오.
EOF

@EOF : 반올림 선택이 정상이 아닙니다. 비선형 성분에 기간 1이없는 반올림 함수는 미쳤습니다.
Eric Towers

2

많은 프로그래머가 다른 언어로 프로그래밍 할 때 배운 관용구를 적용합니다. 모든 언어에 round()기능 이있는 것은 아니며 이러한 언어에서는 floor(x + 0.5)대체로 사용 하는 것이 일반적 입니다. 이러한 프로그래머가 C ++를 사용하기 시작할 때 항상 내장 된 기능이 있다는 것을 깨닫지는 못합니다.round() 익숙한 스타일을 계속 사용합니다.

즉, 무언가를하는 코드가 많다고해서 그럴만 한 이유가 있다는 의미는 아닙니다. 모든 프로그래밍 언어에서 이에 대한 예를 찾을 수 있습니다. 철갑 상어의 법칙을 기억하십시오 :

모든 것의 90 %는 쓰레기

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.