As conversões de ponto flutuante em inteiro estão saturando
As conversões de ponto flutuante em inteiro agora têm comportamento de saturação em computadores x86 e x64. O comportamento de saturação significa que, se o valor convertido for muito pequeno ou grande para o tipo de destino, o valor será definido como o valor mínimo ou máximo, respectivamente, para esse tipo.
Comportamento anterior
A tabela a seguir mostra o comportamento anterior ao converter um valor float
ou double
.
Converter em... | Valor de x |
Resultado (anterior) |
---|---|---|
int escalar e embalado |
int.MinValue <= x <= int.MaxValue |
(int)x |
< int.MinValue ou > int.MaxValue |
int.MinValue |
|
long escalar e embalado |
long.MinValue <= x <= long.MaxValue |
(long)x |
< long.MinValue ou > long.MaxValue |
long.MinValue |
|
uint escalar e embalado |
Qualquer valor | (((long)x << 32) >> 32) |
ulong escalar e embalado |
<= 2^63 |
(long)x |
> 2^63 |
(long)(x - 2^63) + 2^63 |
Novo comportamento
A tabela a seguir mostra o novo comportamento ao converter um valor float
ou double
.
Converter em... | Valor de x |
Resultado do .NET 9+ |
---|---|---|
int escalar e embalado |
int.MinValue <= x <= int.MaxValue |
(int)x |
< int.MinValue |
int.MinValue |
|
> int.MaxValue |
int.MaxValue |
|
NaN |
0 | |
long escalar e embalado |
long.MinValue <= x <= long.MaxValue |
(long)x |
< long.MinValue |
long.MinValue |
|
> long.MaxValue |
long.MaxValue |
|
NaN |
0 | |
uint escalar e embalado |
0 <= x <= uint.MaxValue |
(uint)x |
x > uint.MaxValue |
uint.MaxValue |
|
x < 0 |
0 | |
ulong escalar e embalado |
0 <= x <= ulong.MaxValue |
(ulong)x |
x > ulong.MaxValue |
ulong.MaxValue |
|
x < 0 |
0 |
Versão introduzida
Versão Prévia 4 do .NET 9
Tipo de alteração interruptiva
Esta é uma alteração comportamental.
Motivo da alteração
Essa alteração foi feita para padronizar todas as conversões flutuantes de ponto para inteiro para ter um comportamento de saturação e tornar o comportamento determinístico.
Ação recomendada
Se você confiou nos valores mostrados na seção Comportamento anterior para serem retornados da conversão, mesmo que estivessem incorretos, atualize seu código para esperar os valores mostrados na seção Novo comportamento.
Se a sobrecarga de desempenho do novo comportamento for indesejável para seu cenário, você poderá usar os novos métodos ConvertToIntegerNative<TInteger>
em Single, Double e Half, que são rápidos. Na maioria dos casos, o comportamento desses métodos corresponde ao comportamento anterior de conversão de ponto flutuante em inteiro. No entanto, esses métodos têm um comportamento específico da plataforma que não têm garantia de que vão corresponder ao comportamento de conversão anterior (que já era não determinístico). Em vez disso, esses métodos fazem o que for mais eficiente para a plataforma nativa. Notavelmente, o resultado não é garantido para valores que estão fora do intervalo representável do tipo TInteger
.
No caso incomum em que é necessário desempenho e uma garantia estrita de correspondência com o comportamento de conversão anterior, você pode usar os intrínsecos de hardware específicos da plataforma. Por exemplo, você pode usar Sse.ConvertToInt32(Vector128.CreateScalar(val)) para lidar com (int)val
para float
. Você deve verificar if (Sse.IsSupported)
antes de usar. No entanto, usar esses intrínsecos é complicado, porque outras plataformas de destino (como Arm64) já produzem resultados diferentes.
APIs afetadas
Todas as conversões explícitas e implícitas de ponto flutuante em inteiro:
(int)val
em queval
é umfloat
oudouble
Vector.ConvertToInt32(Vector<float> val)
(long)val
em queval
é umfloat
oudouble
Vector.ConvertToInt64(Vector<double> val)
(uint)val
em queval
é umfloat
oudouble
Vector.ConvertToUInt32(Vector<float> val)
(ulong)val
em queval
é umfloat
oudouble
Vector.ConvertToUInt64(Vector<double> val)