developer tip

float가 모든 int 값을 나타낼 수 없는데 왜 C ++에서 int를 float로 승격합니까?

copycodes 2020. 11. 11. 20:18
반응형

float가 모든 int 값을 나타낼 수 없는데 왜 C ++에서 int를 float로 승격합니까?


다음이 있다고 가정합니다.

int i = 23;
float f = 3.14;
if (i == f) // do something

ia로 승격되고 floatfloat숫자가 비교되지만 a float는 모든 int값을 나타낼 있습니까? int및을 모두 floata로 홍보하지 않으 double시겠습니까?


통합 프로모션에서 int가 승격 되면 unsigned음수 값도 손실됩니다 ( 0u < -1진실 과 같은 재미로 이어짐).

C의 대부분의 메커니즘 (C ++에서 상 속됨)과 마찬가지로 일반적인 산술 변환은 하드웨어 작업 측면에서 이해되어야합니다. C의 제작자는 자신이 작업 한 기계의 어셈블리 언어에 매우 익숙했으며, 그때까지 어셈블리로 작성되었을 작업을 작성할 때 자신과 같은 사람들에게 즉각적인 이해를 제공하기 위해 C를 작성했습니다 (예 : UNIX 핵심).

이제 프로세서는 일반적으로 혼합 유형 명령 (float을 double에 추가, int를 float에 비교 등)이 없습니다. 이는 웨이퍼에서 엄청난 양의 부동산 낭비가 될 것이기 때문입니다. 다양한 유형을 지원하려는만큼 더 많은 opcode. "add int to int", "compare float to float", "multiply unsigned with unsigned"등의 명령어 만 있으면 처음에 필요한 일반적인 산술 변환이 수행됩니다. 두 가지 유형을 명령어에 매핑합니다. 그들과 함께 사용하는 것이 가장 합리적입니다.

저수준 기계어 코드를 작성하는 데 익숙한 사람의 관점에서 볼 때 혼합 유형이있는 경우 일반적으로 고려할 어셈블러 명령어는 최소한의 변환이 필요한 명령어입니다. 이것은 특히 변환이 런타임 비용이 많이 드는 부동 소수점의 경우이며 특히 1970 년대 초, C가 개발되고 컴퓨터가 느 렸고 부동 소수점 계산이 소프트웨어에서 수행되었을 때입니다. 일반 산술 변환에서이 쇼 - 하나의 피연산자 지금까지의 하나의 예외 (변환된다 long/ unsigned int는이 곳 long으로 변환 할 수 있습니다 unsigned long대부분의 기계에서 수행되는 것을 필요로하지 않는, 아마도 어떤 예외가 적용되지 위치에.이 ).

따라서 일반적인 산술 변환은 어셈블리 코더가 대부분의 시간 동안 수행하는 작업을 수행하도록 작성됩니다. 적합하지 않은 두 가지 유형이 있습니다. 하나를 다른 유형으로 변환하여 적합합니다. 이것은 당신이 그렇지 않으면 할 수있는 특정의 이유가하지 않는 한 어셈블러 코드에서 할 거라고 무엇이며, 어셈블러 코드를 작성하는 데 사용하는 사람들에게 명시 적 변환이 자연 것을 요청, 다른 변환을 강제로 특정 이유가 있습니다. 결국, 당신은 간단히 쓸 수 있습니다

if((double) i < (double) f)

그건 그렇고, unsigned계층 구조에서.보다 더 높기 int때문에 intunsigned비교는 부호없는 비교로 끝날 것입니다 (따라서 0u < -1처음부터 비트). 나는 이것이 옛날 사람들 이 가치 범위의 확장보다 unsigned제한으로 덜 고려했다는 지표라고 생각 int합니다. 지금은 기호가 필요하지 않으므로 더 큰 값 범위에 대해 추가 비트를 사용합시다. int16 비트 세계에서 훨씬 더 큰 걱정이 될 것임을 예상 할만한 이유가 있다면 그것을 사용합니다 int.


심지어 double모든 표현하지 못할 수 있습니다 int않습니다 얼마나 많은 비트에 따라 값을 int포함합니다.

int와 float를 모두 double로 승격하지 않는 이유는 무엇입니까?

아마도 double이미 a float피연산자 중 하나를 사용 하는 것보다 두 유형을 모두로 변환하는 것이 더 비싸기 때문일 것 float입니다. 또한 산술 연산자 규칙과 호환되지 않는 비교 연산자에 대한 특수 규칙을 도입합니다.

그것은 블라인드 샷 변환한다고 가정하는 것 때문에 부동 소수점 유형이 표시되는 방법을 보장도 없습니다 intdouble(또는 long double비교하는 것은) 아무것도 해결할 것입니다.


유형 승격 규칙은 단순하고 예측 가능한 방식으로 작동하도록 설계되었습니다. C / C ++의 유형 은 표현할 수 있는 값범위에 따라 자연스럽게 "정렬"됩니다 . 참고 내용은. 부동 소수점 유형은 동일한 수의 유효 자릿수를 나타낼 수 없기 때문에 정수 유형으로 표시되는 모든 정수를 나타낼 수는 없지만 더 넓은 범위를 나타낼 수 있습니다.

예측 가능한 동작을 갖기 위해 유형 승격이 필요할 때 숫자 유형은 항상 더 작은 범위의 오버플로를 방지하기 위해 더 큰 범위 의 유형으로 변환됩니다 . 이것을 상상해보십시오.

int i = 23464364; // more digits than float can represent!
float f = 123.4212E36f; // larger range than int can represent!
if (i == f) { /* do something */ }

정수 유형으로 변환이 수행 된 경우 부동 소수점 f은 int로 변환 될 때 확실히 오버플로되어 정의되지 않은 동작으로 이어집니다. 반면에, 변환 if전용하는 것은 이후 무관 정밀도의 손실의 원인 f이 비교가 성공하는 것이 여전히 가능 그래서 같은 정밀도를 가지고 있습니다. 애플리케이션 요구 사항에 따라 비교 결과를 해석하는 것은 해당 시점의 프로그래머에게 달려 있습니다.

마지막으로 배정 밀도 부동 소수점 숫자가 정수를 나타내는 동일한 문제 (유효 자릿수 제한)를 겪는다는 사실 외에도 두 유형 모두에 승격을 사용하면에 대한 더 높은 정밀도 표현 을 갖게 i되지만 f원래 정밀도를 가질 운명은 다음과 같습니다. 따라서 시작하는 i것보다 유효 숫자가 더 많으면 비교가 성공하지 못합니다 f. 이제 이것은 정의되지 않은 동작입니다. 일부 커플 ( i, f) 에서는 비교가 성공할 수 있지만 다른 커플에서는 성공 하지 못할 수 있습니다.


float모든 int값을 나타낼 있습니까?

int둘 다 float32 비트로 저장 되는 일반적인 최신 시스템의 경우에는 그렇지 않습니다. 뭔가 줄게있어. 32 비트 가치의 정수는 분수를 포함하는 동일한 크기의 집합에 1 : 1로 매핑되지 않습니다.

이 ( i가) a로 승격되고 floatfloat숫자가 비교됩니다.

반드시 그런 것은 아닙니다. 어떤 정밀도가 적용 될지 정말 모릅니다. C ++ 14 §5 / 12 :

부동 피연산자의 값과 부동 표현식의 결과는 유형에 필요한 것보다 더 높은 정밀도와 범위로 표현 될 수 있습니다. 이에 따라 유형은 변경되지 않습니다.

i프로모션 이후에는 명목 유형이 있지만 float값은 double하드웨어 를 사용하여 표시 될 수 있습니다 . C ++는 부동 소수점 정밀도 손실 또는 오버플로를 보장하지 않습니다. (이것은 C ++ 14의 새로운 것이 아닙니다. 예전부터 C에서 상속되었습니다.)

int및을 모두 floata로 홍보하지 않으 double시겠습니까?

모든 곳에서 최적의 정밀도를 원하면 double대신 사용 하면 float. 또는 long double,하지만 느리게 실행될 수 있습니다. 이 규칙은 하나의 기계가 여러 가지 대체 정밀도를 제공 할 수 있다는 점을 고려할 때 제한된 정밀도 유형의 대부분의 사용 사례에 대해 상대적으로 합리적으로 설계되었습니다.

대부분의 경우 빠르고 느슨하면 충분하므로 기계는 가장 쉬운 작업을 자유롭게 수행 할 수 있습니다. 이는 반올림, 단 정밀도 비교 또는 배정 밀도 및 반올림 없음을 의미 할 수 있습니다.

그러나 그러한 규칙은 궁극적으로 타협이며 때로는 실패합니다. C ++ (또는 C)에서 산술을 정확하게 지정하려면 변환 및 승격을 명시 적으로 만드는 것이 도움이됩니다. 매우 신뢰할 수있는 소프트웨어에 대한 많은 스타일 가이드는 암시 적 변환을 모두 사용하는 것을 금지하며 대부분의 컴파일러는이를 정리하는 데 도움이되는 경고를 제공합니다.

이러한 타협이 어떻게 발생했는지 알아 보려면 C rationale 문서를 읽어보십시오 . (최신판은 C99까지 커버합니다.) PDP-11이나 K & R 시대의 무의미한 짐이 아닙니다.


여기에 많은 답변이 C 언어의 기원에서 주장하고 있으며, K & R 및 역사적 수하물을 int가 float와 결합 될 때 float로 변환되는 이유로 명시 적으로 명명되어 있다는 것은 흥미 롭습니다.

이것은 잘못된 당사자를 비난하는 것입니다. K & R C에서는 float 계산 같은 것이 없었습니다. 모든 부동 소수점 연산은 배정 밀도로 수행되었습니다. 따라서 정수 (또는 기타)는 암시 적으로 부동 소수점으로 변환되지 않고 double로만 변환되었습니다. float는 또한 함수 인수의 유형이 될 수 없습니다. 실제로 double 로의 변환을 피하려면 float에 포인터를 전달해야했습니다. 이러한 이유로 기능

int x(float a)
{ ... }

int y(a)
float a;
{ ... }

have different calling conventions. The first gets a float argument, the second (by now no longer permissable as syntax) gets a double argument.

Single-precision floating point arithmetic and function arguments were only introduced with ANSI C. Kernighan/Ritchie is innocent.

Now with the newly available single float expressions (single float previously was only a storage format), there also had to be new type conversions. Whatever the ANSI C team picked here (and I would be at a loss for a better choice) is not the fault of K&R.


Q1: Can a float represent all int values?

IEE754 can represent all integers exactly as floats, up to about 223, as mentioned in this answer.

Q2: Why not promote both the int and the float to a double?

The rules in the Standard for these conversions are slight modifications of those in K&R: the modifications accommodate the added types and the value preserving rules. Explicit license was added to perform calculations in a “wider” type than absolutely necessary, since this can sometimes produce smaller and faster code, not to mention the correct answer more often. Calculations can also be performed in a “narrower” type by the as if rule so long as the same end result is obtained. Explicit casting can always be used to obtain a value in a desired type.

Source

Performing calculations in a wider type means that given float f1; and float f2;, f1 + f2 might be calculated in double precision. And it means that given int i; and float f;, i == f might be calculated in double precision. But it isn't required to calculate i == f in double precision, as hvd stated in the comment.

Also C standard says so. These are known as the usual arithmetic conversions . The following description is taken straight from the ANSI C standard.

...if either operand has type float , the other operand is converted to type float .

Source and you can see it in the ref too.

A relevant link is this answer. A more analytic source is here.

Here is another way to explain this: The usual arithmetic conversions are implicitly performed to cast their values in a common type. Compiler first performs integer promotion, if operands still have different types then they are converted to the type that appears highest in the following hierarchy:

enter image description here

Source.


When a programming language is created some decisions are made intuitively.

For instance why not convert int+float to int+int instead of float+float or double+double? Why call int->float a promotion if it holds the same about of bits? Why not call float->int a promotion?

If you rely on implicit type conversions you should know how they work, otherwise just convert manually.

Some language could have been designed without any automatic type conversions at all. And not every decision during a design phase could have been made logically with a good reason.

JavaScript with it's duck typing has even more obscure decisions under the hood. Designing an absolutely logical language is impossible, I think it goes to Godel incompleteness theorem. You have to balance logic, intuition, practice and ideals.


The question is why: Because it is fast, easy to explain, easy to compile, and these were all very important reasons at the time when the C language was developed.

You could have had a different rule: That for every comparison of arithmetic values, the result is that of comparing the actual numerical values. That would be somewhere between trivial if one of the expressions compared is a constant, one additional instruction when comparing signed and unsigned int, and quite difficult if you compare long long and double and want correct results when the long long cannot be represented as double. (0u < -1 would be false, because it would compare the numerical values 0 and -1 without considering their types).

In Swift, the problem is solved easily by disallowing operations between different types.


The rules are written for 16 bit ints (smallest required size). Your compiler with 32 bit ints surely converts both sides to double. There are no float registers in modern hardware anyway so it has to convert to double. Now if you have 64 bit ints I'm not too sure what it does. long double would be appropriate (normally 80 bits but it's not even standard).

참고URL : https://stackoverflow.com/questions/28010565/why-does-c-promote-an-int-to-a-float-when-a-float-cannot-represent-all-int-val

반응형