다음 선언이있는 경우 :
float a = 3.0 ;
그게 오류인가요? 나는 책에서 읽은 3.0A는 double가치와 내가 가지고로 지정 float a = 3.0f. 그렇습니까?
다음 선언이있는 경우 :
float a = 3.0 ;
그게 오류인가요? 나는 책에서 읽은 3.0A는 double가치와 내가 가지고로 지정 float a = 3.0f. 그렇습니까?
;애프터 가 필요합니다 .
답변:
선언하는 것은 오류가 아닙니다 float a = 3.0. 이렇게하면 컴파일러가 double literal 3.0을 float로 변환합니다.
그러나, 당신은 해야합니다 특정 시나리오에서 부동 소수점 리터럴 표기법을 사용합니다.
성능상의 이유로 :
특히 다음 사항을 고려하십시오.
float foo(float x) { return x * 0.42; }
여기서 컴파일러는 반환 된 각 값에 대해 변환 (런타임에 지불)을 내 보냅니다. 이를 방지하려면 다음을 선언해야합니다.
float foo(float x) { return x * 0.42f; } // OK, no conversion required
결과를 비교할 때 버그를 방지하려면 :
예를 들어 다음 비교가 실패합니다.
float x = 4.2;
if (x == 4.2)
std::cout << "oops"; // Not executed!
float 리터럴 표기법으로 수정할 수 있습니다.
if (x == 4.2f)
std::cout << "ok !"; // Executed!
(참고 : 물론 이것은 일반적으로 평등을 위해 float 또는 double 숫자를 비교하는 방법이 아닙니다. )
올바른 오버로드 된 함수를 호출하려면 (같은 이유로) :
예:
void foo(float f) { std::cout << "\nfloat"; }
void foo(double d) { std::cout << "\ndouble"; }
int main()
{
foo(42.0); // calls double overload
foo(42.0f); // calls float overload
return 0;
}
Cyber 에서 언급했듯이 유형 추론 컨텍스트에서 컴파일러가 다음을 추론하는 데 도움이 필요합니다 float.
다음의 경우 auto:
auto d = 3; // int
auto e = 3.0; // double
auto f = 3.0f; // float
마찬가지로 템플릿 유형 추론의 경우 :
void foo(float f) { std::cout << "\nfloat"; }
void foo(double d) { std::cout << "\ndouble"; }
template<typename T>
void bar(T t)
{
foo(t);
}
int main()
{
bar(42.0); // Deduce double
bar(42.0f); // Deduce float
return 0;
}
42은 정수로, 자동으로 승격되며 float(그리고 괜찮은 컴파일러에서 컴파일 타임에 발생합니다) 성능 저하가 없습니다. 아마도 당신은 같은 것을 의미했을 것 42.0입니다.
4.2이 4.2f있을 수 있으며, FE_INEXACT일부 (확실히 소수) 프로그램은 어떤 부동 소수점 연산이 정확한지, 그렇지 않은지 신경 쓰고 해당 플래그를 테스트합니다. . 이것은 간단하고 명백한 컴파일 타임 변환이 프로그램의 동작을 변경한다는 것을 의미합니다.
float foo(float x) { return x*42.0; }단 정밀도 곱셈으로 컴파일 할 수 있으며 마지막으로 시도했을 때 Clang에 의해 컴파일되었습니다. 그러나 float foo(float x) { return x*0.1; }단일 정밀도 곱셈으로 컴파일 할 수 없습니다. 이 패치 이전에는 약간 지나치게 낙관적이었을 수 있지만 패치 후에는 결과가 항상 같을 때 conversion-double_precision_op-conversion을 single_precision_op으로 결합해야합니다. article.gmane.org/gmane.comp.compilers.llvm.cvs/167800/match=
someFloat표현식 someFloat * 0.1이보다 정확한 결과를 얻을 수 someFloat * 0.1f있지만 대부분의 경우 부동 소수점 나누기보다 저렴합니다. 예를 들어, (float) (167772208.0f * 0.1)은 16777222가 아닌 16777220으로 올바르게 반올림됩니다. 일부 컴파일러는 double부동 소수점 나누기 대신 곱하기를 대체 할 수 있지만 그렇지 않은 컴파일러는 곱하기를 대신 할 수 있습니다 (모든 값은 아니지만 많은 경우에 안전함). ) 곱하기는 유용한 최적화가 될 수 있지만 double역수로 수행되는 경우에만 가능합니다 .
컴파일러는 변수를 float로 선언했기 때문에 다음 리터럴을 float로 변환합니다.
float a = 3; // converted to float
float b = 3.0; // converted to float
float c = 3.0f; // float
auto예를 들어 (또는 다른 유형의 공제 방법) 을 사용한 경우 중요합니다 .
auto d = 3; // int
auto e = 3.0; // double
auto f = 3.0f; // float
auto유일한 경우는 아닙니다.
접미사가없는 부동 소수점 리터럴은 double 유형 이며 C ++ 표준 섹션 2.14.4 부동 리터럴 초안에서 다룹니다 .
[...] 부동 리터럴의 유형은 접미사로 명시 적으로 지정하지 않는 한 double입니다. [...]
그래서 할당 오류가 이중 문자를 A와 플로트 ?3.0
float a = 3.0
아니요, 변환되지 않습니다. 이는 4.8 부동 소수점 변환 섹션에서 다룹니다 .
부동 소수점 유형의 prvalue는 다른 부동 소수점 유형의 prvalue로 변환 될 수 있습니다. 소스 값이 대상 유형에 정확하게 표시 될 수있는 경우 변환 결과는 정확한 표시입니다. 소스 값이 인접한 두 대상 값 사이에있는 경우 변환 결과는 이러한 값 중 하나에 대한 구현 정의 선택입니다. 그렇지 않으면 동작이 정의되지 않습니다.
이것의 의미에 대한 자세한 내용은 GotW # 67 에서 읽을 수 있습니다 .
즉, 정밀도 (즉, 데이터)를 잃더라도 double 상수가 암시 적으로 (즉, 조용히) float 상수로 변환 될 수 있습니다. 이것은 C 호환성 및 사용성 이유 때문에 허용되었지만 부동 소수점 작업을 수행 할 때 명심할 가치가 있습니다.
품질 컴파일러는 정의되지 않은 동작, 즉 float가 나타낼 수있는 최소값보다 작거나 최대 값보다 큰 float에 이중 수량을 넣으려고하면 경고합니다. 정말 좋은 컴파일러는 정의 될 수 있지만 정보를 잃을 수있는 작업을하려고하면 선택적 경고를 제공합니다. 즉, float로 표현할 수있는 최소값과 최대 값 사이에있는 float에 이중 수량을 넣지 만 그렇지 않은 경우 float로 정확하게 표현되어야합니다.
따라서 알아야 할 일반적인 경우에 대한주의 사항이 있습니다.
실용적인 관점에서이 경우 결과는 기술적으로는 변환이 있어도 동일 할 가능성이 높습니다 . godbolt 에서 다음 코드를 시도하여이를 확인할 수 있습니다 .
#include <iostream>
float func1()
{
return 3.0; // a double literal
}
float func2()
{
return 3.0f ; // a float literal
}
int main()
{
std::cout << func1() << ":" << func2() << std::endl ;
return 0;
}
우리는 대한 결과 볼 func1과가 func2모두 사용하여 동일 clang및 gcc:
func1():
movss xmm0, DWORD PTR .LC0[rip]
ret
func2():
movss xmm0, DWORD PTR .LC0[rip]
ret
로 파스칼이 댓글에서 지적 당신은 항상 의지 할 수 없습니다. 0.1및 0.1f각각을 사용 하면 변환이 이제 명시 적으로 수행되어야하므로 생성 된 어셈블리가 달라집니다. 다음 코드 :
float func1(float x )
{
return x*0.1; // a double literal
}
float func2(float x)
{
return x*0.1f ; // a float literal
}
결과는 다음과 같습니다.
func1(float):
cvtss2sd %xmm0, %xmm0 # x, D.31147
mulsd .LC0(%rip), %xmm0 #, D.31147
cvtsd2ss %xmm0, %xmm0 # D.31147, D.31148
ret
func2(float):
mulss .LC2(%rip), %xmm0 #, D.31155
ret
변환이 성능에 영향을 미칠지 여부를 결정할 수 있는지 여부에 관계없이 올바른 유형을 사용하면 의도를 더 잘 문서화 할 수 있습니다. 예를 들어 명시 적 변환을 사용하면 static_cast버그 또는 잠재적 버그를 의미 할 수있는 우발적 인 변환이 아니라 의도 된 변환임을 명확히하는 데 도움이됩니다.
노트
supercat이 지적했듯이 eg 0.1와 곱셈 0.1f은 동등하지 않습니다. 나는 그것이 훌륭했고 요약이 아마 그것을 정의하지 않을 것이기 때문에 그 주석을 인용 할 것입니다.
예를 들어 f가 100000224 (정확히 부동 소수점으로 표현 가능함)와 같으면 1/10을 곱하면 10000022로 내림하는 결과가 생성되지만 대신 0.1f를 곱하면 10000023으로 잘못 반올림되는 결과가 생성됩니다. 만약 의도가 10으로 나누는 것이라면, 곱셈 상수 0.1은 10f로 나누는 것보다 빠르고 0.1f로 나누는 것보다 더 정확할 것입니다.
내 원래 요점은 다른 질문에서 주어진 잘못된 예를 보여주는 것이었지만 이것은 장난감 예에 미묘한 문제가 존재할 수 있음을 잘 보여줍니다.
f = f * 0.1;과 f = f * 0.1f; 다른 일을 한다는 점에 주목할 가치가 있습니다 . 예를 들어, f100000224 (으로 정확하게 표현할 수 있음)와 같으면 float10 분의 1을 곱하면 10000022로 내림되는 결과가 생성되지만 0.1f를 곱하면 10000023으로 잘못 올림되는 결과가 생성됩니다. 의도는 10으로 나누는 double것입니다. 상수 0.1로 곱하면으로 나누는 것보다 빠르며 10f곱하는 것보다 더 정확합니다 0.1f.
컴파일러가 거부한다는 의미에서 오류가 아니라 원하는 것이 아닐 수도 있다는 의미에서 오류입니다.
책에서 올바르게 언급했듯이은 3.0유형 값입니다 double. 이 암시 적 변환은 출신 double에 float, 그래서 float a = 3.0;변수의 올바른 정의입니다.
그러나 적어도 개념적으로 이것은 불필요한 변환을 수행합니다. 컴파일러에 따라 변환은 컴파일 타임에 수행되거나 런타임을 위해 저장 될 수 있습니다. 런타임을 위해 저장하는 유효한 이유는 부동 소수점 변환이 어렵고 값을 정확하게 표현할 수없는 경우 예기치 않은 부작용이 발생할 수 있으며 값을 정확하게 표현할 수 있는지 여부를 확인하는 것이 항상 쉬운 것은 아니기 때문입니다.
3.0f 기술적으로 컴파일러는 여전히 런타임에 상수를 계산할 수 있지만 (항상 그렇습니다) 여기에서는 컴파일러가 그렇게 할 수있는 이유가 전혀 없습니다.
다음을 시도하는 경우 :
std::cout << sizeof(3.2f) <<":" << sizeof(3.2) << std::endl;
다음과 같이 출력됩니다.
4:8
즉, 3.2f의 크기는 32 비트 시스템에서 4 바이트로 간주되며 3.2는 32 비트 시스템에서 8 바이트를 사용하는 double 값으로 해석됩니다. 이것은 당신이 찾고있는 답을 제공 할 것입니다.
double 및 float다른, 그것은 당신이를 초기화 할 수 있는지 여부를 응답하지 않는 float이중 문자에서
컴파일러는 리터럴에서 가장 적합한 유형을 추론하거나 최소한 가장 적합하다고 생각하는 유형을 추론합니다. 이는 정밀도보다 효율성을 잃는 것입니다. 즉, float 대신 double을 사용합니다. 확실하지 않은 경우 brace-intializer를 사용하여 명시 적으로 만듭니다.
auto d = double{3}; // make a double
auto f = float{3}; // make a float
auto i = int{3}; // make a int
유형 변환 규칙이 적용되는 다른 변수에서 초기화하면 이야기가 더 흥미로워집니다. 이중 형식을 리터럴로 구성하는 것이 합법적이지만 가능한 축소없이 int에서 구성 할 수 없습니다.
auto xxx = double{i} // warning ! narrowing conversion of 'i' from 'int' to 'double'
3.0을 float로 변환합니다 . 최종 결과는와 구별 할 수 없습니다float a = 3.0f.