/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Portable/Utilities/ValueSetFactory.DecimalTC.cs
196 строк
9 KB
Julien Couvreur
Patterns: Allow non-negative and full integer sets to merge (intersect or union) (#71968)
06 мар 2024, 06:45
Не верифицирован
06 мар 2024, 06:45
3ae3ef9
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Diagnostics; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { using static BinaryOperatorKind; internal static partial class ValueSetFactory { private class DecimalTC : INumericTC<decimal> { public static readonly DecimalTC Instance = new DecimalTC(); // These are the smallest nonzero normal mantissa value (in three parts) below which you could use a higher scale. // This is the 96-bit representation of ((2^96)-1) / 10; private const uint transitionLow = 0x99999999; private const uint transitionMid = 0x99999999; private const uint transitionHigh = 0x19999999; private const byte maxScale = 28; private static readonly decimal normalZero = new decimal(lo: 0, mid: 0, hi: 0, isNegative: false, scale: maxScale); private static readonly decimal epsilon = new decimal(lo: 1, mid: 0, hi: 0, isNegative: false, scale: maxScale); decimal INumericTC<decimal>.MinValue => decimal.MinValue; decimal INumericTC<decimal>.MaxValue => decimal.MaxValue; decimal INumericTC<decimal>.Zero => 0M; public decimal FromConstantValue(ConstantValue constantValue) => constantValue.IsBad ? 0m : constantValue.DecimalValue; public ConstantValue ToConstantValue(decimal value) => ConstantValue.Create(value); public decimal Next(decimal value) { Debug.Assert(value != decimal.MaxValue); if (value == 0m) return epsilon; var (low, mid, high, isNegative, scale) = DecimalRep.FromValue(value); Debug.Assert(scale == DecimalRep.FromValue(value).Normalize().scale); // assert that the input is normalized if (isNegative) { // get the next value closer to zero.(less negative) if (value == -epsilon) return normalZero; // skip negative zero // This should not occur, as numbers such as this are not in our normal form (not at maximum scale). Debug.Assert(!(scale < 28 && low == transitionLow && mid == transitionMid && high == transitionHigh)); if (low != 0) return new DecimalRep(low: low - 1, mid: mid, high: high, isNegative: isNegative, scale: scale).Value; if (mid != 0) return new DecimalRep(low: uint.MaxValue, mid: mid - 1, high: high, isNegative: isNegative, scale: scale).Value; Debug.Assert(high > 0); // otherwise value == 0m return new DecimalRep(low: uint.MaxValue, mid: uint.MaxValue, high: high - 1, isNegative: isNegative, scale: scale).Value; } else { // get the next value farther from zero.(more positive) if (low != uint.MaxValue) return new DecimalRep(low: low + 1, mid: mid, high: high, isNegative: isNegative, scale: scale).Value; if (mid != uint.MaxValue) return new DecimalRep(low: 0, mid: mid + 1, high: high, isNegative: isNegative, scale: scale).Value; if (high != uint.MaxValue) return new DecimalRep(low: 0, mid: 0, high: high + 1, isNegative: isNegative, scale: scale).Value; // the mantissa it at its maximum value. Divide the mantissa by 10 and decrease the scale. // Since we know the value of the mantissa, we can simply assign mantissa/10 here. low = transitionLow; mid = transitionMid; high = transitionHigh; Debug.Assert(scale > 0); // otherwise value == decimal.MaxValue scale -= 1; var result = new DecimalRep(low: low + 1, mid: mid, high: high, isNegative: isNegative, scale: scale).Value; // Assert that the value returned really is the next possible value. Debug.Assert(new DecimalRep(low: low, mid: mid, high: high, isNegative: isNegative, scale: scale).Value <= value); Debug.Assert(result > value); return result; } } bool INumericTC<decimal>.Related(BinaryOperatorKind relation, decimal left, decimal right) { switch (relation) { case Equal: return left == right; case GreaterThanOrEqual: return left >= right; case GreaterThan: return left > right; case LessThanOrEqual: return left <= right; case LessThan: return left < right; default: throw new ArgumentException("relation"); } } string INumericTC<decimal>.ToString(decimal value) => FormattableString.Invariant($"{value:G}"); decimal INumericTC<decimal>.Prev(decimal value) { return -Next(-value); } public decimal Random(Random random) { INumericTC<uint> uinttc = UIntTC.Instance; return new DecimalRep( low: uinttc.Random(random), mid: uinttc.Random(random), high: uinttc.Random(random), isNegative: random.NextDouble() < 0.5, scale: (byte)random.Next(0, maxScale + 1)).Normalize().Value; } public static decimal Normalize(decimal value) => DecimalRep.FromValue(value).Normalize().Value; private readonly struct DecimalRep { public readonly uint low; public readonly uint mid; public readonly uint high; public readonly bool isNegative; public readonly byte scale; public DecimalRep(uint low, uint mid, uint high, bool isNegative, byte scale) { if (scale > maxScale) throw new ArgumentException("scale"); this.low = low; this.mid = mid; this.high = high; this.isNegative = isNegative; this.scale = scale; } public decimal Value => new decimal(lo: (int)low, mid: (int)mid, hi: (int)high, isNegative: isNegative, scale: scale); public DecimalRep Normalize() { // return the number in the highest possible scale (containing the most precision) if (this.scale == maxScale) return this; var (low, mid, high, isNegative, scale) = this; while (scale < maxScale) { if (high * 10L > uint.MaxValue) break; long newHigh = 10L * high; long newMid = 10L * mid; long newLow = 10L * low; newMid += newLow >> 32; newLow &= uint.MaxValue; newHigh += newMid >> 32; newMid &= uint.MaxValue; if (newHigh > uint.MaxValue) break; low = (uint)newLow; mid = (uint)newMid; high = (uint)newHigh; scale += 1; } return new DecimalRep(low, mid, high, isNegative, scale); } public static DecimalRep FromValue(decimal value) { value.GetBits(out bool isNegative, out byte scale, out uint low, out uint mid, out uint high); Debug.Assert(scale <= maxScale); return new DecimalRep(low: low, mid: mid, high: high, isNegative: isNegative, scale: scale); } public void Deconstruct(out uint low, out uint mid, out uint high, out bool isNegative, out byte scale) => (low, mid, high, isNegative, scale) = (this.low, this.mid, this.high, this.isNegative, this.scale); public override string ToString() => $"Decimal({(isNegative ? "-" : "+")}, 0x{high:08X} 0x{mid:08X} 0x{low:08X} *10^-{scale})"; } } } }