Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Conversies tussen Decimal en binaire drijvendekommatypen en conversies van BigInteger naar binaire drijvendekommatypen produceren nu correct afgeronde resultaten. Voorheen konden deze conversies afkappen, via tussenliggende waarden afronden of significante cijfers verwijderen.
Geïntroduceerde versie
.NET 11 preview 7
Vorig gedrag
Conversies van float en double naar decimal behielden slechts respectievelijk 7 en 15 significante decimalen. Dit kan het verschil verbergen tussen een binaire drijvende-kommaliteral en een decimale literal:
using System.Globalization;
double value = 1.23;
decimal converted = (decimal)value;
Console.WriteLine(converted.ToString("G29", CultureInfo.InvariantCulture));
De uitvoer was:
1.23
Conversies van decimal naar float of double kunnen meerdere keren worden afgerond. Voorbeeld:
using System.Globalization;
decimal value = 10000000000000.099609375m;
double converted = (double)value;
Console.WriteLine(converted.ToString("G99", CultureInfo.InvariantCulture));
De uitvoer was:
10000000000000.09765625
Conversies van decimal naar Half of BFloat16 converteerden eerst de waarde naar float, wat ook een onjuist afgerond resultaat kan opleveren.
Conversies van BigInteger naar double kapten bits af in plaats van af te ronden naar de dichtstbijzijnde representeerbare waarde. Voorbeeld:
using System.Globalization;
using System.Numerics;
BigInteger value = long.MaxValue / 2;
double converted = (double)value;
Console.WriteLine(converted.ToString("G17", CultureInfo.InvariantCulture));
De uitvoer was:
4.6116860184273874E+18
Conversies van BigInteger naar float, Half of BFloat16 converteerden de waarde eerst naar double, waardoor er tweemaal kon worden afgerond en een resultaat kon ontstaan dat één eenheid in de minst significante positie afwijkt van de dichtstbijzijnde representeerbare waarde.
Nieuw gedrag
Vanaf .NET 11 ronden conversies de exacte bronwaarde eenmaal af naar de dichtstbijzijnde vertegenwoordigbare doelwaarde.
In het eerste voorbeeld is de double waarde niet precies 1.23. De exacte waarde is ongeveer 1.229999999999999982236431605997..., dus de geconverteerde decimal waarde is nu:
1.229999999999999982236431606
In het tweede voorbeeld is de geconverteerde double waarde nu:
10000000000000.099609375
Conversies van decimal naar BFloat16 of Half worden ook correct afgerond en kunnen een ander bitpatroon van de bestemming opleveren dan in eerdere versies.
In het BigInteger voorbeeld is de geconverteerde double waarde nu:
4.6116860184273879E+18
Conversies van BigInteger naar float, Halfof BFloat16 worden ook correct afgerond.
Als een conversie wordt geëvalueerd als een compilatieconstante, kan een compiler die wordt gehost door de .NET 11 Preview 7 SDK of een latere SDK het nieuwe resultaat insluiten in de uitvoerassembly wanneer het project opnieuw wordt opgebouwd, ongeacht het doelframework van het project.
Type van brekende verandering
Deze wijziging is een gedragswijziging.
Reden voor wijziging
De vorige conversiealgoritmen verloren informatie die het doeltype zou kunnen vertegenwoordigen en soms een andere waarde dan het dichtstbijzijnde vertegenwoordigbare resultaat hebben geselecteerd. Dit veroorzaakte precisiefouten in beide richtingen van decimal conversie en in conversies van BigInteger. De nieuwe algoritmen volgen de verwachte drijvendekommaregel van de berekening van de conversie alsof met exacte tussenliggende precisie, gevolgd door één afronding naar het doeltype.
Zie dotnet/runtime#130565 en dotnet/runtime#130566 voor meer informatie.
Aanbevolen actie
Ga er niet van uit dat een binaire drijvendekommaliteral en een decimale literal met dezelfde broncode dezelfde waarde voorstellen. Als een waarde bedoeld is om decimaal te zijn, gebruikt u een letterlijke decimale waarde:
decimal value = 123.4567m;
in plaats van een conversie vanuit een double letterlijke waarde:
decimal value = (decimal)123.4567;
Als u het vorige resultaat in het algemeen wilt herstellen wanneer u converteert naar decimal, rondt u de geconverteerde waarde af naar 7 significante decimalen voor een float bron of 15 significante decimale cijfers voor een double bron. De vorige conversie is afgerond op het dichtstbijzijnde met gelijke bindingen; Het is niet afgekapt. Positieve en negatieve waarden werden symmetrisch verwerkt.
Als u bijvoorbeeld de bronwaarde opmaakt met de G7 tekenreeks of G15 de standaardtekenreeks voor numerieke notatie en het resultaat parseert, wordt de bijbehorende afronding van significante cijfers uitgevoerd voor willekeurige eindige waarden:
using System.Globalization;
static decimal ConvertToDecimalLikePrevious(float value)
{
Span<char> text = stackalloc char[32];
value.TryFormat(text, out int length, "G7", CultureInfo.InvariantCulture);
return decimal.Parse(text[..length], NumberStyles.Float, CultureInfo.InvariantCulture);
}
static decimal ConvertToDecimalLikePrevious(double value)
{
Span<char> text = stackalloc char[32];
value.TryFormat(text, out int length, "G15", CultureInfo.InvariantCulture);
return decimal.Parse(text[..length], NumberStyles.Float, CultureInfo.InvariantCulture);
}
decimal fromFloat = ConvertToDecimalLikePrevious(14.1f); // 14.1
decimal fromDouble = ConvertToDecimalLikePrevious(-123.4567); // -123.4567
De span-gebaseerde implementatie alloceert geen geheugen. Waarden buiten het bereik van decimal blijven tijdens het parseren een uitzondering genereren, net zoals tijdens de conversie.
Werk de tests en de geserialiseerde verwachte waarden bij die het vorige, onjuist afgeronde resultaat bevatten. Dit omvat code die afhankelijk is BigInteger van afkapping van conversie naar nul. Als voor een toepassing een specifiek verouderdfloat, doubleof HalfBFloat16 bitpatroon voor een protocol of bestandsindeling is vereist, codeert u dat bitpatroon expliciet in plaats van het patroon te reproduceren via een numerieke conversie.
Er is geen compatibiliteitsswitch om de vorige conversiealgoritmen te herstellen.
Betreffende API's
- Decimal.Decimal(Single)
- Decimal.Decimal(Double)
- Expliciete conversies van Single en Double naar Decimal
- Expliciete conversies van Decimal naar Single en Double
- Decimal.ToSingle(Decimal)
- Decimal.ToDouble(Decimal)
- Convert.ToDecimal(Single)
- Convert.ToDecimal(Double)
- Convert.ToSingle(Decimal)
- Convert.ToDouble(Decimal)
- Decimal.CreateChecked<TOther>(TOther), Decimal.CreateSaturating<TOther>(TOther) en Decimal.CreateTruncating<TOther>(TOther) wanneer ze naar of van Single of Double worden geconverteerd
- Expliciete conversie van Decimal naar Half
- Expliciete conversie van Decimal naar BFloat16
- Expliciete conversies van BigInteger naar Double, Singleen HalfBFloat16
- Equivalente conversies uitgevoerd via IConvertible of de algemene wiskundige interfaces