C #의 정수 나누기가 왜 부동 소수점이 아닌 정수를 반환합니까?


131

C #의 정수 나누기가 왜 실수가 아닌 정수를 반환하는지 아는 사람이 있습니까? 그 뒤에 아이디어는 무엇입니까? (C / C ++의 유산입니까?)

C #에서 :

float x = 13 / 4;   
//== operator is overridden here to use epsilon compare
if (x == 3.0)
   print 'Hello world';

이 코드의 결과는 다음과 같습니다.

'Hello world'

엄밀히 말하면 정수 나누기와 같은 것은 없습니다.


46
integer분할이 아니기 때문에 floating point분할입니다.
헌터 맥밀런

그것은 (VB.Net에서) 분할 연산의 모든 결과가 비합리적 인 수학적 방식으로 다르게 구현되어야합니다.
BanditoBunny

3
나는 당신이 합리적인 숫자 를 의미한다고 생각합니다 . Wikipedia 참조 : 두 정수를 나누면 나머지가 생길 수 있습니다. 나머지의 나눗셈을 완료하기 위해 숫자 시스템은 분수 또는 합리적인 숫자가 더 일반적으로 불릴 때 포함되도록 확장됩니다.
crashmstr 2016 년

6
이것이 제가 언어의 "구문 복사"를 좋아하지 않는 이유입니다. VB는 "C #은 C와 같다"가 아니라 "C #은 .NET"이라고 생각합니다. 내 실수는 추측하지만이 경우에는 VB 방식을 선호합니다. 초기화되지 않은 간단한 유형을 사용할 때 컴파일러 오류를 생성하는 데 어려움을 겪은 경우 (C에서도 경고가 표시되지 않음) 정수 나누기를 부동 소수점에 할당 할 때 경고하지 않는 이유는 무엇입니까?
darda

답변:


96

새로운 프로그래머가 실제로 부동 소수점 나누기를 사용하려고 할 때 정수 나누기를 수행하는 실수를 저지르는 것이 일반적이지만 실제로는 정수 나누기가 매우 일반적인 작업입니다. 사람들이 거의 사용하지 않는다고 가정하고 나누기를 할 때마다 항상 부동 소수점으로 캐스팅해야한다는 것을 알고 있다면 실수입니다.

먼저 정수 나누기가 훨씬 빠르므로 정수 결과 만 필요한 경우 더 효율적인 알고리즘을 사용하려고합니다.

둘째, 정수 나누기를 사용하는 많은 알고리즘이 있으며 나누기 결과가 항상 부동 소수점 수인 경우 매번 결과를 반올림해야합니다. 내 머리 꼭대기의 한 예는 숫자의 밑을 바꾸는 것입니다. 각 자릿수를 계산하려면 숫자의 부동 소수점 나누기보다는 숫자의 정수 나누기와 나머지를 나눕니다.

이러한 (및 기타 관련) 이유로 인해 정수 나누기는 정수가됩니다. 두 정수의 부동 소수점 나누기를 얻으려면 하나를 double/ float/ 로 캐스팅해야합니다 decimal.


5
VB.Net에서 .Net 아키텍트는 또 다른 결정을 내 렸습니다 : /-항상 float 나누기, \-integer 나누기. 따라서 C ++ 레거시를 고려하는 경우를 제외하고는 일관성이 없습니다.
BanditoBunny

4
컴파일 타임에 /연산자가 정수 또는 부동 소수점 나누기를 수행 할지 여부를 결정할 수 있습니다 (동적을 사용하지 않는 한). 이 어려운 경우를 위해 당신은 당신이 한 줄에 너무 많은 일을하고 있기 때문에 나는 그렇게는 쉽게 피연산자는 정수 또는 부동 소수점 형식인지 파악하는 것이 여러 줄에 그 선을 깨는 게 좋을 것, 그것을 알아낼 수 있습니다. 미래의 코드 독자들은 그것을 감사하게 생각할 것입니다.
Servy

5
나는 개인적으로 내가 나누는 변수가 무엇인지 항상 생각해야한다는 것이 문제가된다는 것을 알았습니다. 나는 그것을주의를 낭비하는 것으로 간주합니다.
BanditoBunny

8
@pelesl 천문학적으로 많은 수의 프로그램이 손상 될 수있는 엄청난 변화가 있기 때문에 C #에서는 결코 일어나지 않을 것이라고 확신 할 수 있습니다. 그것은 언어로 1 일부터해야하거나 전혀하지 않는 것입니다.
Servy

2
@Servy : C, C ++ 및 C #에는 많은 것들이 있습니다. 개인적으로 정수 나누기에 다른 연산자가 있으면 C #이 더 나은 언어라고 생각하고 합법적 인 코드 생성이 놀라운 동작을 피하기 위해 int/int연산자는 단순히 불법적이었습니다. 원하는 행동에 따른 다른 연산자]. 정수 나눗셈에 사용할 수있는 다른 좋은 토큰 시퀀스가 ​​있었으므로 /해당 용도로 사용을 더 이상 사용 하지 않을 수는 있지만 실제적인 것이 무엇인지 모르겠습니다.
supercat

77

C # 사양을 참조하십시오 . 나누기 연산자에는 세 가지 유형이 있습니다

  • 정수 나누기
  • 부동 소수점 나누기
  • 십진수 나누기

귀하의 경우 다음 규칙이 적용되는 정수 부서가 있습니다.

나누기는 결과를 0으로 반올림하고 결과의 절대 값은 두 피연산자의 몫의 절대 값보다 작은 가능한 가장 큰 정수입니다. 두 피연산자가 동일한 부호를 갖는 경우 결과는 0 또는 양수이고 두 피연산자가 반대 부호를 갖는 경우 결과는 0 또는 음수입니다.

C #이 정수 (일부 언어는 부동 결과를 반환)에 이러한 유형의 나누기를 사용하는 이유는 하드웨어입니다. 정수 나누기가 더 빠르고 간단합니다.


어떤 언어가 부동 결과를 반환합니까? @SergeyBerezovskiy
Ilaria

40

각 데이터 유형은 각 연산자를 오버로드 할 수 있습니다. 분자와 분모가 모두 정수인 경우 정수 유형은 나누기 연산을 수행하고 정수 유형을 리턴합니다. 부동 소수점 나누기를 원하는 경우 하나 이상의 숫자를 부동 소수점 유형으로 캐스팅 한 다음 나누십시오. 예를 들어 :

int x = 13;
int y = 4;
float x = (float)y / (float)z;

또는 리터럴을 사용하는 경우 :

float x = 13f / 4f;

부동 소수점은 정확하지 않습니다. 정밀도에 관심이 있다면 십진법과 같은 것을 사용하십시오.


1
부동 소수점 나누기를 만들기 위해 단 하나의 항만 부동 소수점이어야한다고 언급하면 ​​+1입니다.
Xynariz

분명히 정확성에 대한 당신의 진술은 학습의 맥락에서 이해하기 어렵게 만드는 권리입니다. IEE 754-1985에 따르면 정확한 결과를 얻을 수 있습니다. (대부분의 경우는 아니지만) 정확한 결과를 얻을 수 있습니다. 계산 값이 이전에 정확히 제시되었고 결과가 간단히 말하면 2의 거듭 제곱의 합인 경우 정확한 결과를 얻을 수 있습니다. 이러한 특수한 경우 정밀도에 의존하는 것이 가장 좋은 방법은 아니지만.
L. Monty

정밀도에 대한 추가 : 결과가 1 또는 -1에 가까울수록 정확한 결과를 얻을 수있는 가능성이 크게 향상됩니다. 무한한 숫자와 유한 한 수의 결과가 있기 때문에이 propability가 여전히 0으로 남아 있다는 것이 약간 혼란 스러울 수 있습니다. :)
L. Monty

1
@ L.Monty 감사합니다. 이 답변을 작성한 후 부동 소수점에 대해 더 많이 배웠으며 귀하가 만드는 점은 공정합니다. 기술적으로, 나는 여전히“부동 소수점이 정확하지 않다”라는 진술은 받아 들일 수 있다고 말하고 싶습니다. 어떤 것이 정확할 수 있다고해서 때로는 전체적으로 정확하다는 의미는 아닙니다. 그들이 말했듯이, 깨진 시계는 하루에 두 번 옳습니다. 그러나 나는 결코 정밀 기기라고 부르지 않을 것입니다. 실제로 십진법 정확 하다는 제 제안보다 더 귀찮게 한 부분이라는 사실에 놀랐습니다 .
Steven Doggart

부동 소수점과 같은 이유로 소수는 정확하지 않습니다. 부동 소수점은 밑이 2이고 소수점은 밑이 10입니다. 예를 들어, 10 진수 유형은 정확한 1/3 값을 정확하게 보유 할 수 없습니다.
Steven Doggart

11

당신이 어떤 접미사를 사용하지 않기 때문에, 리터럴 13과는 4정수로 해석됩니다 :

설명서 :

리터럴 접미사가없는 경우, 그것의 값이 표현 될 수있는 이러한 유형의 첫 번째가 : int, uint, long, ulong.

따라서 13정수로 선언 하므로 정수 나누기가 수행됩니다.

설명서 :

x / y 형식의 연산의 경우 이진 연산자 과부하 해결이 적용되어 특정 연산자 구현을 선택합니다. 피연산자는 선택한 연산자의 매개 변수 유형으로 변환되며 결과 유형은 연산자의 리턴 유형입니다.

사전 정의 된 부서 연산자는 다음과 같습니다. 연산자는 모두 x와 y의 몫을 계산합니다.

정수 나누기 :

int operator /(int x, int y);
uint operator /(uint x, uint y);
long operator /(long x, long y);
ulong operator /(ulong x, ulong y);

그리고 반올림이 발생합니다.

나누기는 결과를 0으로 반올림하고 결과의 절대 값은 두 피연산자의 몫의 절대 값보다 작은 가능한 가장 큰 정수입니다. 두 피연산자가 동일한 부호를 갖는 경우 결과는 0 또는 양수이고 두 피연산자가 반대 부호를 갖는 경우 결과는 0 또는 음수입니다.

다음을 수행하는 경우 :

int x = 13f / 4f;

부동 소수점 나누기 ( /연산자의 연산자) 때문에 컴파일러 오류가 발생합니다.13f )는 부동 소수점을 초래하므로 암시 적으로 int로 캐스트 할 수 없으므로 합니다.

나누기가 부동 소수점 나누기가 되려면 결과를 부동 소수점으로 만들어야합니다.

float x = 13 / 4;

여전히 정수를 나눌 것입니다.이 정수는 내재적으로 float로 캐스팅됩니다 3.0. 결과는입니다 . f접미사 ( 13f, 4f)를 사용하여 피연산자를 float로 명시 적으로 선언합니다 .


부동 소수점으로 답을 가질 수는 있지만 여전히 정수 나누기를 수행한다고 설명하는 +1. 또한 부동 소수점 나누기를 강제하는 다른 일반적인 방법은 나누기의 첫 번째 항에를 곱하는 것 1.0입니다.
Xynariz

8

그것은 단지 기본적인 작업 입니다.

나누는 법을 기억하십시오. 처음에 우리는 해결했다 9/6 = 1 with remainder 3.

9 / 6 == 1  //true
9 % 6 == 3 // true

/ 연산자와 % 연산자를 조합하여 해당 값을 검색합니다.


6

유용 할 수 있습니다.

double a = 5.0/2.0;   
Console.WriteLine (a);      // 2.5

double b = 5/2;   
Console.WriteLine (b);      // 2

int c = 5/2;   
Console.WriteLine (c);      // 2

double d = 5f/2f;   
Console.WriteLine (d);      // 2.5

답변에 대한 설명을 추가해보십시오
NetStarter

마지막 표현은 2.5아닙니다 2.
Lasse V. Karlsen

그렇습니다. 감사.
eozten

4

결과는 항상 분자와 분모의 범위가 더 큰 유형입니다. int (Int32)를 생성하는 byte 및 short는 예외입니다.

var a = (byte)5 / (byte)2;  // 2 (Int32)
var b = (short)5 / (byte)2; // 2 (Int32)
var c = 5 / 2;              // 2 (Int32)
var d = 5 / 2U;             // 2 (UInt32)
var e = 5L / 2U;            // 2 (Int64)
var f = 5L / 2UL;           // 2 (UInt64)
var g = 5F / 2UL;           // 2.5 (Single/float)
var h = 5F / 2D;            // 2.5 (Double)
var i = 5.0 / 2F;           // 2.5 (Double)
var j = 5M / 2;             // 2.5 (Decimal)
var k = 5M / 2F;            // Not allowed

부동 소수점 유형과 10 진수 유형 간에는 암시 적 변환이 없으므로 이들을 구분할 수 없습니다. 원하는 것을 명시 적으로 캐스팅하고 결정해야합니다 (소수점은 부동 소수점 유형에 비해 정밀도가 높고 범위가 작습니다).

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