Le conversioni da virgola mobile a integer stanno saturando

Ora le conversioni da virgola mobile a integer hanno un comportamento di saturazione nei computer x86 e x64. Il comportamento di saturazione significa che se il valore convertito è troppo piccolo o troppo grande per il tipo di destinazione il valore viene impostato rispettivamente sul valore minimo o massimo per il tipo.

In .NET 11, questo comportamento si estende alle conversioni non controllate in sbyte, byte, short, ushort e char su CoreCLR (incluso il suo interprete) e Native AOT, ma non su Mono. Per i dettagli, vedi Le conversioni in virgola mobile in tipi integrali di piccole dimensioni sono con saturazione.

Comportamento precedente

Nella tabella seguente viene illustrato il comportamento precedente durante la conversione di un valore float o double.

Converti in ... Valore di x Risultato (precedente)
int scalare e compattato int.MinValue <= x <= int.MaxValue (int)x
< int.MinValue oppure > int.MaxValue int.MinValue
long scalare e compattato long.MinValue <= x <= long.MaxValue (long)x
< long.MinValue oppure > long.MaxValue long.MinValue
uint scalare e compresso Qualsiasi valore (((long)x << 32) >> 32)
ulong scalare e compattato <= 2^63 (long)x
> 2^63 (long)(x - 2^63) + 2^63

Nuovo comportamento

Nella tabella seguente viene illustrato il nuovo comportamento durante la conversione di un valore float o double.

Converti in ... Valore di x Risultato di .NET 9+
int scalare e compattato int.MinValue <= x <= int.MaxValue (int)x
< int.MinValue int.MinValue
> int.MaxValue int.MaxValue
NaN 0
long scalare e impacchettato long.MinValue <= x <= long.MaxValue (long)x
< long.MinValue long.MinValue
> long.MaxValue long.MaxValue
NaN 0
uint scalare e impacchettato 0 <= x <= uint.MaxValue (uint)x
x > uint.MaxValue uint.MaxValue
x < 0 0
ulong scalare e compresso 0 <= x <= ulong.MaxValue (ulong)x
x > ulong.MaxValue ulong.MaxValue
x < 0 0

Versione introdotta

.NET 9 Preview 4

Tipo di modifica che causa un'interruzione

Questa è una modifica funzionale.

Motivo della modifica

Questa modifica è stata apportata per standardizzare tutte le conversioni da virgola mobile a integer in modo da avere un comportamento di saturazione e rendere il comportamento deterministico.

Se ci si è basati sui valori mostrati nella sezione Comportamento precedente come valori restituiti dalla conversione, anche se errati, aggiornare il codice in modo che si aspetti i valori mostrati nella sezione Nuovo comportamento.

Se l'overhead in termini di prestazioni del nuovo comportamento non è desiderabile nel tuo scenario, puoi usare invece i nuovi metodi ConvertToIntegerNative<TInteger> di Single, Double e Half, che sono veloci. Nella maggior parte dei casi, il comportamento di questi metodi corrisponde al comportamento precedente di conversione da virgola mobile a integer precedente. Tuttavia, questi metodi hanno un comportamento specifico della piattaforma che non è garantito che corrisponda al comportamento di conversione precedente (che già non era deterministico). Questi metodi eseguono invece le operazioni più efficienti per la piattaforma nativa. In particolare, il risultato non è garantito per i valori che non rientrano nell'intervallo rappresentabile del tipo TInteger.

Nel caso insolito in cui servissero prestazioni e una rigorosa garanzia di corrispondenza del comportamento di conversione precedente, è possibile usare gli oggetti intrinseci hardware specifici della piattaforma. Ad esempio, si può usare Sse.ConvertToInt32(Vector128.CreateScalar(val)) per gestire (int)val per float. È necessario verificare if (Sse.IsSupported) prima dell'uso. Usare queste intrinseche è complicato, però, perché altre piattaforme di destinazione (come Arm64) producono già risultati diversi.

API interessate

Tutti i cast espliciti e impliciti dal virgola mobile a integer:

  • (int)val dove val è un float o double
  • Vector.ConvertToInt32(Vector<float> val)
  • (long)val dove val è un float o double
  • Vector.ConvertToInt64(Vector<double> val)
  • (uint)val dove val è un float o double
  • Vector.ConvertToUInt32(Vector<float> val)
  • (ulong)val dove val è un float o double
  • Vector.ConvertToUInt64(Vector<double> val)