부동소수점에서 작은 정수 형식으로의 변환은 포화됩니다.

.NET 11에서는 sbyte, byte, short, ushort, char로의 unchecked 부동 소수점 변환이 이제 대상 형식의 경계값에서 포화 동작을 합니다. 너무 작거나 너무 큰 값은 각각 대상 형식의 최소값 또는 최대값으로 설정됩니다.

이 변경은 .NET 9의 부동 소수점-정수 변환 변경을 이어가며, float 및 int에서 double, uint, long, ulong으로의 변환을 표준화했습니다. .NET 11은 채도를 8비트 및 16비트 대상으로 확장합니다.

변경 내용은 인터프리터 및 네이티브 AOT를 포함하여 CoreCLR에 적용됩니다. Mono는 이 변경 내용에 포함되지 않습니다.

자세한 내용은 dotnet/runtime#128604 참조하세요.

도입된 버전

.NET 11 미리 보기 7

이전 동작

이전에는 값이 대상 형식의 범위를 초과하거나 NaN인 경우, 검사되지 않은 부동 소수점 값을 정수 형식으로 변환한 결과를 .NET에서 보장하지 않았습니다. 결과는 CoreCLR 및 Mono와 같은 런타임 구현, x86, x64, Arm32, Arm64 및 WebAssembly와 같은 아키텍처와 x87, SSE2, AVX 및 AVX-512와 같은 아키텍처 내의 하드웨어 명령 집합 간에 다를 수 있습니다.

.NET 9의 호환성이 깨지는 변경에서는 특히 x86 및 x64를 지목했는데, 이 경우 변환이 오버플로 시 일반적으로 센티널 값을 반환했습니다. Arm64는 이미 관례적으로 포화 변환을 사용해 왔다. 이 변경은 기존 센티널 결과를 명세상의 계약으로 삼기보다는 더 넓은 정수 형식으로의 변환을 표준화했습니다.

작은 정수 형식의 경우, .NET 9 및 .NET 10에서 일반적인 CoreCLR 변환 시퀀스는 먼저 int로 포화 변환한 다음, 상위 비트를 버려 대상 형식으로 축소하는 것이었습니다. 이 일련의 과정은 많은 애플리케이션에서 나타난 동작을 설명하지만, 부동소수점을 작은 정수형으로 직접 캐스팅하는 것에 대한 보장된 규약은 아니었습니다.

다음 표에서는 런타임 float 또는 double 값 x에 대한 2단계 시퀀스의 결과를 보여줍니다. 이러한 입력은 int에 들어가므로, 예제는 대상의 하위 8비트 또는 16비트를 제외한 모든 비트를 버리는 효과만 분리해 보여 줍니다. 보존된 비트는 sbyte 및 short에 대해서는 부호 있는 것으로 해석되고, ushort, char, 및 byte에 대해서는 부호 없는 것으로 해석됩니다. char 결과는 숫자로 표시됩니다.

으로 변환 x의 값 낮은 비트 유지 이전 결과 예제
sbyte 또는 byte 298 0x2A 42
sbyte -298 0xD6 -42
byte -42 0xD6 214
short, ushort 또는 char 65578 0x002A 42
short -65578 0xFFD6 -42
ushort 또는 char -42 0xFFD6 65494

예를 들어 다음 코드는 다음을 반환할 수 있습니다.42

static short ConvertValue(double value)
{
    return unchecked((short)value);
}

short result = ConvertValue(65578.0);

중간 int은 0x0001002A(65578)입니다. 하위 16비트만 유지되므로 결과는 0x002A(short.MaxValue)이며, 42로 포화되는 것이 아닙니다.

새 동작

.NET 11부터 선택되지 않은 변환은 대상 형식의 범위에서 포화 상태가 됩니다. 대상 범위 내의 유한 값은 계속 0으로 반올림됩니다. NaN 는 0으로 변환됩니다.

으로 변환 음의 무한대를 포함한 최소값 미만 양수 무한대를 포함하여 최대값 이상 NaN
sbyte -128(sbyte.MinValue) 127 (sbyte.MaxValue) 0
byte 0(byte.MinValue) 255 (byte.MaxValue) 0
short -32768 (short.MinValue) 32767 (short.MaxValue) 0
ushort 0(ushort.MinValue) 65535 (ushort.MaxValue) 0
char 0(char.MinValue) 65535 (char.MaxValue) 0

이제 앞의 예제는 42 대신 short.MaxValue(32767)를 반환합니다. 마찬가지로, 이제 298에서 -42로의 변환은 65494 대신 ushort를 반환하고, 0에서 255로의 변환은 byte 대신 42를 반환합니다.

float를 통해 변환되므로 Half로부터의 해당하는 검사되지 않은 변환도 새 동작을 사용합니다. ConvertToInteger<TInteger>(Single) 및 ConvertToInteger<TInteger>(Double)는 이제 이러한 작은 대상 타입에 대해 올바르게 포화됩니다.

확인된 변환은 변경되지 않으며 변환이 오버플로될 때 계속 throw OverflowException 됩니다. 이 변경은 정수에서 정수로의 축소 변환 또는 .NET 9 변경에서 다루는 더 넓은 정수 및 벡터 변환을 변경하지 않습니다.

파괴적 변경 유형

이 변경 사항은 동작 변경입니다.

변경 이유

.NET 9 변경은 더 넓은 정수 형식으로 변환하기 위한 포화 동작을 설정했지만 8비트 및 16비트 대상으로의 변환에는 범위 외 값NaN에 대한 하드웨어 종속 및 구현 종속 동작이 여전히 있습니다. 이 변경은 이러한 변환을 결정적이고 포화적인 동작으로 만들고 JIT, CoreCLR 인터프리터 및 네이티브 AOT 미리 초기화된 값이 동의하게 합니다.

코드가 범위를 벗어난 입력에 대해 이전 결과를 사용하는 경우 가능한 경우 대상 형식의 범위에서 채도를 예상하도록 업데이트합니다.

이러한 변경 전에 일반적으로 사용되는 플랫폼 네이티브 동작이 필요한 경우 가장 간단한 해결 방법은 다음과 ConvertToIntegerNative<TInteger>(Double)입니다ConvertToIntegerNative<TInteger>(Single). 예를 들어, 가 인 경우 직접 캐스트를 로 바꾸고, 인 경우에는 로 바꿉니다.

중간 변환을 명시적으로 선택할 수도 있습니다. 다음 예에서는 x 입력 ushort 및 double 대상을 사용합니다:

필수 동작 Conversion
대개 이전 동작을 복원하는 대상 타입으로의 플랫폼 기본 변환 double.ConvertToIntegerNative<ushort>(x)
int까지 포화된 다음 범위를 좁히며, 일반적인 .NET 9 및 .NET 10 CoreCLR 시퀀스와 일치합니다. unchecked((ushort)(int)x)
플랫폼 네이티브 형식에서 int로 변환한 다음 축소 변환하는 것으로, .NET 9 이전에 일반적으로 쓰이던 시퀀스와 같습니다 unchecked((ushort)double.ConvertToIntegerNative<int>(x))

float.ConvertToIntegerNative 입력에는 float을 사용하고 byte, char, short 또는 sbyte에는 적절한 대상 유형을 대신 사용합니다.

.NET 9 변경과 마찬가지로 ConvertToIntegerNative는 범위를 벗어난 값 또는 에 대해 NaN. 런타임, 아키텍처 또는 하드웨어 수정 버전에서 변경할 수 있는 현재 플랫폼에 효율적인 동작을 선택합니다. 명시적 (ushort)(int)x는 그 대신 int로의 포화 변환을 선택한 다음 정수 축소를 수행하며, 과거의 모든 구현의 동작을 복원하지는 않습니다.

변환된 값이 배열 인덱스, 버퍼 오프셋 또는 길이로 사용되는 경우 결과 값이 필요한 범위 내에 있는지 확인합니다. 한 컴퓨터에서 사용 가능한 값을 생성하는 변환은 플랫폼 네이티브 변환이 다른 컴퓨터에서 수행되도록 설정하지 않습니다.

영향을 받는 API