|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Buffers.Binary;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace System
{
[Serializable]
[StructLayout(LayoutKind.Sequential)]
[TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public readonly struct Int32
: IComparable,
IConvertible,
ISpanFormattable,
IComparable<int>,
IEquatable<int>,
IBinaryInteger<int>,
IMinMaxValue<int>,
ISignedNumber<int>,
IUtf8SpanFormattable,
IBinaryIntegerParseAndFormatInfo<int>
{
private readonly int m_value; // Do not rename (binary serialization)
public const int MaxValue = 0x7fffffff;
public const int MinValue = unchecked((int)0x80000000);
/// <summary>Represents the additive identity (0).</summary>
private const int AdditiveIdentity = 0;
/// <summary>Represents the multiplicative identity (1).</summary>
private const int MultiplicativeIdentity = 1;
/// <summary>Represents the number one (1).</summary>
private const int One = 1;
/// <summary>Represents the number zero (0).</summary>
private const int Zero = 0;
/// <summary>Represents the number negative one (-1).</summary>
private const int NegativeOne = -1;
/// <summary>Produces the full product of two 32-bit numbers.</summary>
/// <param name="left">The first number to multiply.</param>
/// <param name="right">The second number to multiply.</param>
/// <returns>The number containing the product of the specified numbers.</returns>
public static long BigMul(int left, int right) => Math.BigMul(left, right);
// Compares this object to another object, returning an integer that
// indicates the relationship.
// Returns :
// 0 if the values are equal
// Negative number if _value is less than value
// Positive number if _value is more than value
// null is considered to be less than any instance, hence returns positive number
// If object is not of type Int32, this method throws an ArgumentException.
//
public int CompareTo(object? value)
{
if (value == null)
{
return 1;
}
// NOTE: Cannot use return (_value - value) as this causes a wrap
// around in cases where _value - value > MaxValue.
if (value is int i)
{
if (m_value < i) return -1;
if (m_value > i) return 1;
return 0;
}
throw new ArgumentException(SR.Arg_MustBeInt32);
}
public int CompareTo(int value)
{
// NOTE: Cannot use return (_value - value) as this causes a wrap
// around in cases where _value - value > MaxValue.
if (m_value < value) return -1;
if (m_value > value) return 1;
return 0;
}
public override bool Equals([NotNullWhen(true)] object? obj)
{
if (!(obj is int))
{
return false;
}
return m_value == ((int)obj).m_value;
}
[NonVersionable]
public bool Equals(int obj)
{
return m_value == obj;
}
// The absolute value of the int contained.
public override int GetHashCode()
{
return m_value;
}
public override string ToString()
{
return Number.Int32ToDecStr(m_value);
}
public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format)
{
return ToString(format, null);
}
public string ToString(IFormatProvider? provider)
{
return Number.FormatInt32(m_value, 0, null, provider);
}
public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider)
{
return Number.FormatInt32(m_value, ~0, format, provider);
}
public bool TryFormat(Span<char> destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
{
return Number.TryFormatInt32(m_value, ~0, format, provider, destination, out charsWritten);
}
/// <inheritdoc cref="IUtf8SpanFormattable.TryFormat" />
public bool TryFormat(Span<byte> utf8Destination, out int bytesWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
{
return Number.TryFormatInt32(m_value, ~0, format, provider, utf8Destination, out bytesWritten);
}
public static int Parse(string s) => Parse(s, NumberStyles.Integer, provider: null);
public static int Parse(string s, NumberStyles style) => Parse(s, style, provider: null);
public static int Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider);
public static int Parse(string s, NumberStyles style, IFormatProvider? provider)
{
if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); }
return Parse(s.AsSpan(), style, provider);
}
public static int Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
return Number.ParseBinaryInteger<char, int>(s, style, NumberFormatInfo.GetInstance(provider));
}
public static bool TryParse([NotNullWhen(true)] string? s, out int result) => TryParse(s, NumberStyles.Integer, provider: null, out result);
public static bool TryParse(ReadOnlySpan<char> s, out int result) => TryParse(s, NumberStyles.Integer, provider: null, out result);
/// <summary>Tries to convert a UTF-8 character span containing the string representation of a number to its 32-bit signed integer equivalent.</summary>
/// <param name="utf8Text">A span containing the UTF-8 characters representing the number to convert.</param>
/// <param name="result">When this method returns, contains the 32-bit signed integer value equivalent to the number contained in <paramref name="utf8Text" /> if the conversion succeeded, or zero if the conversion failed. This parameter is passed uninitialized; any value originally supplied in result will be overwritten.</param>
/// <returns><c>true</c> if <paramref name="utf8Text" /> was converted successfully; otherwise, false.</returns>
public static bool TryParse(ReadOnlySpan<byte> utf8Text, out int result) => TryParse(utf8Text, NumberStyles.Integer, provider: null, out result);
public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out int result)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
if (s is null)
{
result = 0;
return false;
}
return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
}
public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out int result)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
}
//
// IConvertible implementation
//
public TypeCode GetTypeCode()
{
return TypeCode.Int32;
}
bool IConvertible.ToBoolean(IFormatProvider? provider)
{
return Convert.ToBoolean(m_value);
}
char IConvertible.ToChar(IFormatProvider? provider)
{
return Convert.ToChar(m_value);
}
sbyte IConvertible.ToSByte(IFormatProvider? provider)
{
return Convert.ToSByte(m_value);
}
byte IConvertible.ToByte(IFormatProvider? provider)
{
return Convert.ToByte(m_value);
}
short IConvertible.ToInt16(IFormatProvider? provider)
{
return Convert.ToInt16(m_value);
}
ushort IConvertible.ToUInt16(IFormatProvider? provider)
{
return Convert.ToUInt16(m_value);
}
int IConvertible.ToInt32(IFormatProvider? provider)
{
return m_value;
}
uint IConvertible.ToUInt32(IFormatProvider? provider)
{
return Convert.ToUInt32(m_value);
}
long IConvertible.ToInt64(IFormatProvider? provider)
{
return Convert.ToInt64(m_value);
}
ulong IConvertible.ToUInt64(IFormatProvider? provider)
{
return Convert.ToUInt64(m_value);
}
float IConvertible.ToSingle(IFormatProvider? provider)
{
return Convert.ToSingle(m_value);
}
double IConvertible.ToDouble(IFormatProvider? provider)
{
return Convert.ToDouble(m_value);
}
decimal IConvertible.ToDecimal(IFormatProvider? provider)
{
return Convert.ToDecimal(m_value);
}
DateTime IConvertible.ToDateTime(IFormatProvider? provider)
{
throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Int32", "DateTime"));
}
object IConvertible.ToType(Type type, IFormatProvider? provider)
{
return Convert.DefaultToType((IConvertible)this, type, provider);
}
//
// IAdditionOperators
//
/// <inheritdoc cref="IAdditionOperators{TSelf, TOther, TResult}.op_Addition(TSelf, TOther)" />
static int IAdditionOperators<int, int, int>.operator +(int left, int right) => left + right;
/// <inheritdoc cref="IAdditionOperators{TSelf, TOther, TResult}.op_Addition(TSelf, TOther)" />
static int IAdditionOperators<int, int, int>.operator checked +(int left, int right) => checked(left + right);
//
// IAdditiveIdentity
//
/// <inheritdoc cref="IAdditiveIdentity{TSelf, TResult}.AdditiveIdentity" />
static int IAdditiveIdentity<int, int>.AdditiveIdentity => AdditiveIdentity;
//
// IBinaryInteger
//
/// <inheritdoc cref="IBinaryInteger{TSelf}.DivRem(TSelf, TSelf)" />
public static (int Quotient, int Remainder) DivRem(int left, int right) => Math.DivRem(left, right);
/// <inheritdoc cref="IBinaryInteger{TSelf}.LeadingZeroCount(TSelf)" />
[Intrinsic]
public static int LeadingZeroCount(int value) => BitOperations.LeadingZeroCount((uint)value);
/// <inheritdoc cref="IBinaryInteger{TSelf}.PopCount(TSelf)" />
[Intrinsic]
public static int PopCount(int value) => BitOperations.PopCount((uint)value);
/// <inheritdoc cref="IBinaryInteger{TSelf}.RotateLeft(TSelf, int)" />
[Intrinsic]
public static int RotateLeft(int value, int rotateAmount) => (int)BitOperations.RotateLeft((uint)value, rotateAmount);
/// <inheritdoc cref="IBinaryInteger{TSelf}.RotateRight(TSelf, int)" />
[Intrinsic]
public static int RotateRight(int value, int rotateAmount) => (int)BitOperations.RotateRight((uint)value, rotateAmount);
/// <inheritdoc cref="IBinaryInteger{TSelf}.TrailingZeroCount(TSelf)" />
[Intrinsic]
public static int TrailingZeroCount(int value) => BitOperations.TrailingZeroCount(value);
/// <inheritdoc cref="IBinaryInteger{TSelf}.TryReadBigEndian(ReadOnlySpan{byte}, bool, out TSelf)" />
static bool IBinaryInteger<int>.TryReadBigEndian(ReadOnlySpan<byte> source, bool isUnsigned, out int value)
{
int result = default;
if (source.Length != 0)
{
// Propagate the most significant bit so we have `0` or `-1`
sbyte sign = (sbyte)(source[0]);
sign >>= 31;
Debug.Assert((sign == 0) || (sign == -1));
// We need to also track if the input data is unsigned
isUnsigned |= (sign == 0);
if (isUnsigned && sbyte.IsNegative(sign) && (source.Length >= sizeof(int)))
{
// When we are unsigned and the most significant bit is set, we are a large positive
// and therefore definitely out of range
value = result;
return false;
}
if (source.Length > sizeof(int))
{
if (source[..^sizeof(int)].ContainsAnyExcept((byte)sign))
{
// When we are unsigned and have any non-zero leading data or signed with any non-set leading
// data, we are a large positive/negative, respectively, and therefore definitely out of range
value = result;
return false;
}
if (isUnsigned == sbyte.IsNegative((sbyte)source[^sizeof(int)]))
{
// When the most significant bit of the value being set/clear matches whether we are unsigned
// or signed then we are a large positive/negative and therefore definitely out of range
value = result;
return false;
}
}
ref byte sourceRef = ref MemoryMarshal.GetReference(source);
if (source.Length >= sizeof(int))
{
sourceRef = ref Unsafe.Add(ref sourceRef, source.Length - sizeof(int));
// We have at least 4 bytes, so just read the ones we need directly
result = Unsafe.ReadUnaligned<int>(ref sourceRef);
if (BitConverter.IsLittleEndian)
{
result = BinaryPrimitives.ReverseEndianness(result);
}
}
else
{
// We have between 1 and 3 bytes, so construct the relevant value directly
// since the data is in Big Endian format, we can just read the bytes and
// shift left by 8-bits for each subsequent part
for (int i = 0; i < source.Length; i++)
{
result <<= 8;
result |= Unsafe.Add(ref sourceRef, i);
}
if (!isUnsigned)
{
result |= ((1 << ((sizeof(int) * 8) - 1)) >> (((sizeof(int) - source.Length) * 8) - 1));
}
}
}
value = result;
return true;
}
/// <inheritdoc cref="IBinaryInteger{TSelf}.TryReadLittleEndian(ReadOnlySpan{byte}, bool, out TSelf)" />
static bool IBinaryInteger<int>.TryReadLittleEndian(ReadOnlySpan<byte> source, bool isUnsigned, out int value)
{
int result = default;
if (source.Length != 0)
{
// Propagate the most significant bit so we have `0` or `-1`
sbyte sign = (sbyte)(source[^1]);
sign >>= 31;
Debug.Assert((sign == 0) || (sign == -1));
// We need to also track if the input data is unsigned
isUnsigned |= (sign == 0);
if (isUnsigned && sbyte.IsNegative(sign) && (source.Length >= sizeof(int)))
{
// When we are unsigned and the most significant bit is set, we are a large positive
// and therefore definitely out of range
value = result;
return false;
}
if (source.Length > sizeof(int))
{
if (source[sizeof(int)..].ContainsAnyExcept((byte)sign))
{
// When we are unsigned and have any non-zero leading data or signed with any non-set leading
// data, we are a large positive/negative, respectively, and therefore definitely out of range
value = result;
return false;
}
if (isUnsigned == sbyte.IsNegative((sbyte)source[sizeof(int) - 1]))
{
// When the most significant bit of the value being set/clear matches whether we are unsigned
// or signed then we are a large positive/negative and therefore definitely out of range
value = result;
return false;
}
}
ref byte sourceRef = ref MemoryMarshal.GetReference(source);
if (source.Length >= sizeof(int))
{
// We have at least 4 bytes, so just read the ones we need directly
result = Unsafe.ReadUnaligned<int>(ref sourceRef);
if (!BitConverter.IsLittleEndian)
{
result = BinaryPrimitives.ReverseEndianness(result);
}
}
else
{
// We have between 1 and 3 bytes, so construct the relevant value directly
// since the data is in Little Endian format, we can just read the bytes and
// shift left by 8-bits for each subsequent part, then reverse endianness to
// ensure the order is correct. This is more efficient than iterating in reverse
// due to current JIT limitations
for (int i = 0; i < source.Length; i++)
{
result <<= 8;
result |= Unsafe.Add(ref sourceRef, i);
}
result <<= ((sizeof(int) - source.Length) * 8);
result = BinaryPrimitives.ReverseEndianness(result);
if (!isUnsigned)
{
result |= ((1 << ((sizeof(int) * 8) - 1)) >> (((sizeof(int) - source.Length) * 8) - 1));
}
}
}
value = result;
return true;
}
/// <inheritdoc cref="IBinaryInteger{TSelf}.GetShortestBitLength()" />
int IBinaryInteger<int>.GetShortestBitLength()
{
int value = m_value;
if (value >= 0)
{
return (sizeof(int) * 8) - LeadingZeroCount(value);
}
else
{
return (sizeof(int) * 8) + 1 - LeadingZeroCount(~value);
}
}
/// <inheritdoc cref="IBinaryInteger{TSelf}.GetByteCount()" />
int IBinaryInteger<int>.GetByteCount() => sizeof(int);
/// <inheritdoc cref="IBinaryInteger{TSelf}.TryWriteBigEndian(Span{byte}, out int)" />
bool IBinaryInteger<int>.TryWriteBigEndian(Span<byte> destination, out int bytesWritten)
{
if (BinaryPrimitives.TryWriteInt32BigEndian(destination, m_value))
{
bytesWritten = sizeof(int);
return true;
}
bytesWritten = 0;
return false;
}
/// <inheritdoc cref="IBinaryInteger{TSelf}.TryWriteLittleEndian(Span{byte}, out int)" />
bool IBinaryInteger<int>.TryWriteLittleEndian(Span<byte> destination, out int bytesWritten)
{
if (BinaryPrimitives.TryWriteInt32LittleEndian(destination, m_value))
{
bytesWritten = sizeof(int);
return true;
}
bytesWritten = 0;
return false;
}
//
// IBinaryNumber
//
/// <inheritdoc cref="IBinaryNumber{TSelf}.AllBitsSet" />
static int IBinaryNumber<int>.AllBitsSet => NegativeOne;
/// <inheritdoc cref="IBinaryNumber{TSelf}.IsPow2(TSelf)" />
public static bool IsPow2(int value) => BitOperations.IsPow2(value);
/// <inheritdoc cref="IBinaryNumber{TSelf}.Log2(TSelf)" />
[Intrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Log2(int value)
{
if (value < 0)
{
ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException();
}
return BitOperations.Log2((uint)value);
}
//
// IBitwiseOperators
//
/// <inheritdoc cref="IBitwiseOperators{TSelf, TOther, TResult}.op_BitwiseAnd(TSelf, TOther)" />
static int IBitwiseOperators<int, int, int>.operator &(int left, int right) => left & right;
/// <inheritdoc cref="IBitwiseOperators{TSelf, TOther, TResult}.op_BitwiseOr(TSelf, TOther)" />
static int IBitwiseOperators<int, int, int>.operator |(int left, int right) => left | right;
/// <inheritdoc cref="IBitwiseOperators{TSelf, TOther, TResult}.op_ExclusiveOr(TSelf, TOther)" />
static int IBitwiseOperators<int, int, int>.operator ^(int left, int right) => left ^ right;
/// <inheritdoc cref="IBitwiseOperators{TSelf, TOther, TResult}.op_OnesComplement(TSelf)" />
static int IBitwiseOperators<int, int, int>.operator ~(int value) => ~value;
//
// IComparisonOperators
//
/// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_LessThan(TSelf, TOther)" />
static bool IComparisonOperators<int, int, bool>.operator <(int left, int right) => left < right;
/// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_LessThanOrEqual(TSelf, TOther)" />
static bool IComparisonOperators<int, int, bool>.operator <=(int left, int right) => left <= right;
/// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_GreaterThan(TSelf, TOther)" />
static bool IComparisonOperators<int, int, bool>.operator >(int left, int right) => left > right;
/// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_GreaterThanOrEqual(TSelf, TOther)" />
static bool IComparisonOperators<int, int, bool>.operator >=(int left, int right) => left >= right;
//
// IDecrementOperators
//
/// <inheritdoc cref="IDecrementOperators{TSelf}.op_Decrement(TSelf)" />
static int IDecrementOperators<int>.operator --(int value) => --value;
/// <inheritdoc cref="IDecrementOperators{TSelf}.op_Decrement(TSelf)" />
static int IDecrementOperators<int>.operator checked --(int value) => checked(--value);
//
// IDivisionOperators
//
/// <inheritdoc cref="IDivisionOperators{TSelf, TOther, TResult}.op_Division(TSelf, TOther)" />
static int IDivisionOperators<int, int, int>.operator /(int left, int right) => left / right;
//
// IEqualityOperators
//
/// <inheritdoc cref="IEqualityOperators{TSelf, TOther, TResult}.op_Equality(TSelf, TOther)" />
static bool IEqualityOperators<int, int, bool>.operator ==(int left, int right) => left == right;
/// <inheritdoc cref="IEqualityOperators{TSelf, TOther, TResult}.op_Inequality(TSelf, TOther)" />
static bool IEqualityOperators<int, int, bool>.operator !=(int left, int right) => left != right;
//
// IIncrementOperators
//
/// <inheritdoc cref="IIncrementOperators{TSelf}.op_Increment(TSelf)" />
static int IIncrementOperators<int>.operator ++(int value) => ++value;
/// <inheritdoc cref="IIncrementOperators{TSelf}.op_CheckedIncrement(TSelf)" />
static int IIncrementOperators<int>.operator checked ++(int value) => checked(++value);
//
// IMinMaxValue
//
/// <inheritdoc cref="IMinMaxValue{TSelf}.MinValue" />
static int IMinMaxValue<int>.MinValue => MinValue;
/// <inheritdoc cref="IMinMaxValue{TSelf}.MaxValue" />
static int IMinMaxValue<int>.MaxValue => MaxValue;
//
// IModulusOperators
//
/// <inheritdoc cref="IModulusOperators{TSelf, TOther, TResult}.op_Modulus(TSelf, TOther)" />
static int IModulusOperators<int, int, int>.operator %(int left, int right) => left % right;
//
// IMultiplicativeIdentity
//
/// <inheritdoc cref="IMultiplicativeIdentity{TSelf, TResult}.MultiplicativeIdentity" />
static int IMultiplicativeIdentity<int, int>.MultiplicativeIdentity => MultiplicativeIdentity;
//
// IMultiplyOperators
//
/// <inheritdoc cref="IMultiplyOperators{TSelf, TOther, TResult}.op_Multiply(TSelf, TOther)" />
static int IMultiplyOperators<int, int, int>.operator *(int left, int right) => left * right;
/// <inheritdoc cref="IMultiplyOperators{TSelf, TOther, TResult}.op_CheckedMultiply(TSelf, TOther)" />
static int IMultiplyOperators<int, int, int>.operator checked *(int left, int right) => checked(left * right);
//
// INumber
//
/// <inheritdoc cref="INumber{TSelf}.Clamp(TSelf, TSelf, TSelf)" />
public static int Clamp(int value, int min, int max) => Math.Clamp(value, min, max);
/// <inheritdoc cref="INumber{TSelf}.CopySign(TSelf, TSelf)" />
public static int CopySign(int value, int sign)
{
int absValue = value;
if (absValue < 0)
{
absValue = -absValue;
}
if (sign >= 0)
{
if (absValue < 0)
{
Math.ThrowNegateTwosCompOverflow();
}
return absValue;
}
return -absValue;
}
/// <inheritdoc cref="INumber{TSelf}.Max(TSelf, TSelf)" />
public static int Max(int x, int y) => Math.Max(x, y);
/// <inheritdoc cref="INumber{TSelf}.MaxNumber(TSelf, TSelf)" />
static int INumber<int>.MaxNumber(int x, int y) => Max(x, y);
/// <inheritdoc cref="INumber{TSelf}.Min(TSelf, TSelf)" />
public static int Min(int x, int y) => Math.Min(x, y);
/// <inheritdoc cref="INumber{TSelf}.MinNumber(TSelf, TSelf)" />
static int INumber<int>.MinNumber(int x, int y) => Min(x, y);
/// <inheritdoc cref="INumber{TSelf}.Sign(TSelf)" />
public static int Sign(int value) => Math.Sign(value);
//
// INumberBase
//
/// <inheritdoc cref="INumberBase{TSelf}.One" />
static int INumberBase<int>.One => One;
/// <inheritdoc cref="INumberBase{TSelf}.Radix" />
static int INumberBase<int>.Radix => 2;
/// <inheritdoc cref="INumberBase{TSelf}.Zero" />
static int INumberBase<int>.Zero => Zero;
/// <inheritdoc cref="INumberBase{TSelf}.Abs(TSelf)" />
public static int Abs(int value) => Math.Abs(value);
/// <inheritdoc cref="INumberBase{TSelf}.CreateChecked{TOther}(TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CreateChecked<TOther>(TOther value)
where TOther : INumberBase<TOther>
{
int result;
if (typeof(TOther) == typeof(int))
{
result = (int)(object)value;
}
else if (!TryConvertFromChecked(value, out result) && !TOther.TryConvertToChecked(value, out result))
{
ThrowHelper.ThrowNotSupportedException();
}
return result;
}
/// <inheritdoc cref="INumberBase{TSelf}.CreateSaturating{TOther}(TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CreateSaturating<TOther>(TOther value)
where TOther : INumberBase<TOther>
{
int result;
if (typeof(TOther) == typeof(int))
{
result = (int)(object)value;
}
else if (!TryConvertFromSaturating(value, out result) && !TOther.TryConvertToSaturating(value, out result))
{
ThrowHelper.ThrowNotSupportedException();
}
return result;
}
/// <inheritdoc cref="INumberBase{TSelf}.CreateTruncating{TOther}(TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CreateTruncating<TOther>(TOther value)
where TOther : INumberBase<TOther>
{
int result;
if (typeof(TOther) == typeof(int))
{
result = (int)(object)value;
}
else if (!TryConvertFromTruncating(value, out result) && !TOther.TryConvertToTruncating(value, out result))
{
ThrowHelper.ThrowNotSupportedException();
}
return result;
}
/// <inheritdoc cref="INumberBase{TSelf}.IsCanonical(TSelf)" />
static bool INumberBase<int>.IsCanonical(int value) => true;
/// <inheritdoc cref="INumberBase{TSelf}.IsComplexNumber(TSelf)" />
static bool INumberBase<int>.IsComplexNumber(int value) => false;
/// <inheritdoc cref="INumberBase{TSelf}.IsEvenInteger(TSelf)" />
public static bool IsEvenInteger(int value) => (value & 1) == 0;
/// <inheritdoc cref="INumberBase{TSelf}.IsFinite(TSelf)" />
static bool INumberBase<int>.IsFinite(int value) => true;
/// <inheritdoc cref="INumberBase{TSelf}.IsImaginaryNumber(TSelf)" />
static bool INumberBase<int>.IsImaginaryNumber(int value) => false;
/// <inheritdoc cref="INumberBase{TSelf}.IsInfinity(TSelf)" />
static bool INumberBase<int>.IsInfinity(int value) => false;
/// <inheritdoc cref="INumberBase{TSelf}.IsInteger(TSelf)" />
static bool INumberBase<int>.IsInteger(int value) => true;
/// <inheritdoc cref="INumberBase{TSelf}.IsNaN(TSelf)" />
static bool INumberBase<int>.IsNaN(int value) => false;
/// <inheritdoc cref="INumberBase{TSelf}.IsNegative(TSelf)" />
public static bool IsNegative(int value) => value < 0;
/// <inheritdoc cref="INumberBase{TSelf}.IsNegativeInfinity(TSelf)" />
static bool INumberBase<int>.IsNegativeInfinity(int value) => false;
/// <inheritdoc cref="INumberBase{TSelf}.IsNormal(TSelf)" />
static bool INumberBase<int>.IsNormal(int value) => value != 0;
/// <inheritdoc cref="INumberBase{TSelf}.IsOddInteger(TSelf)" />
public static bool IsOddInteger(int value) => (value & 1) != 0;
/// <inheritdoc cref="INumberBase{TSelf}.IsPositive(TSelf)" />
public static bool IsPositive(int value) => value >= 0;
/// <inheritdoc cref="INumberBase{TSelf}.IsPositiveInfinity(TSelf)" />
static bool INumberBase<int>.IsPositiveInfinity(int value) => false;
/// <inheritdoc cref="INumberBase{TSelf}.IsRealNumber(TSelf)" />
static bool INumberBase<int>.IsRealNumber(int value) => true;
/// <inheritdoc cref="INumberBase{TSelf}.IsSubnormal(TSelf)" />
static bool INumberBase<int>.IsSubnormal(int value) => false;
/// <inheritdoc cref="INumberBase{TSelf}.IsZero(TSelf)" />
static bool INumberBase<int>.IsZero(int value) => (value == 0);
/// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitude(TSelf, TSelf)" />
public static int MaxMagnitude(int x, int y)
{
int absX = x;
if (absX < 0)
{
absX = -absX;
if (absX < 0)
{
return x;
}
}
int absY = y;
if (absY < 0)
{
absY = -absY;
if (absY < 0)
{
return y;
}
}
if (absX > absY)
{
return x;
}
if (absX == absY)
{
return IsNegative(x) ? y : x;
}
return y;
}
/// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitudeNumber(TSelf, TSelf)" />
static int INumberBase<int>.MaxMagnitudeNumber(int x, int y) => MaxMagnitude(x, y);
/// <inheritdoc cref="INumberBase{TSelf}.MinMagnitude(TSelf, TSelf)" />
public static int MinMagnitude(int x, int y)
{
int absX = x;
if (absX < 0)
{
absX = -absX;
if (absX < 0)
{
return y;
}
}
int absY = y;
if (absY < 0)
{
absY = -absY;
if (absY < 0)
{
return x;
}
}
if (absX < absY)
{
return x;
}
if (absX == absY)
{
return IsNegative(x) ? x : y;
}
return y;
}
/// <inheritdoc cref="INumberBase{TSelf}.MinMagnitudeNumber(TSelf, TSelf)" />
static int INumberBase<int>.MinMagnitudeNumber(int x, int y) => MinMagnitude(x, y);
/// <inheritdoc cref="INumberBase{TSelf}.MultiplyAddEstimate(TSelf, TSelf, TSelf)" />
static int INumberBase<int>.MultiplyAddEstimate(int left, int right, int addend) => (left * right) + addend;
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromChecked{TOther}(TOther, out TSelf)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<int>.TryConvertFromChecked<TOther>(TOther value, out int result) => TryConvertFromChecked(value, out result);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryConvertFromChecked<TOther>(TOther value, out int result)
where TOther : INumberBase<TOther>
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
// `ConvertTo` handle the opposite sign. However, since there is an uneven split
// between signed and unsigned types, the one that handles unsigned will also
// handle `Decimal`.
//
// That is, `ConvertFrom` for `int` will handle the other signed types and
// `ConvertTo` will handle the unsigned types
if (typeof(TOther) == typeof(double))
{
double actualValue = (double)(object)value;
result = checked((int)actualValue);
return true;
}
else if (typeof(TOther) == typeof(Half))
{
Half actualValue = (Half)(object)value;
result = checked((int)actualValue);
return true;
}
else if (typeof(TOther) == typeof(short))
{
short actualValue = (short)(object)value;
result = actualValue;
return true;
}
else if (typeof(TOther) == typeof(long))
{
long actualValue = (long)(object)value;
result = checked((int)actualValue);
return true;
}
else if (typeof(TOther) == typeof(Int128))
{
Int128 actualValue = (Int128)(object)value;
result = checked((int)actualValue);
return true;
}
else if (typeof(TOther) == typeof(nint))
{
nint actualValue = (nint)(object)value;
result = checked((int)actualValue);
return true;
}
else if (typeof(TOther) == typeof(sbyte))
{
sbyte actualValue = (sbyte)(object)value;
result = actualValue;
return true;
}
else if (typeof(TOther) == typeof(float))
{
float actualValue = (float)(object)value;
result = checked((int)actualValue);
return true;
}
else
{
result = default;
return false;
}
}
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromSaturating{TOther}(TOther, out TSelf)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<int>.TryConvertFromSaturating<TOther>(TOther value, out int result) => TryConvertFromSaturating(value, out result);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryConvertFromSaturating<TOther>(TOther value, out int result)
where TOther : INumberBase<TOther>
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
// `ConvertTo` handle the opposite sign. However, since there is an uneven split
// between signed and unsigned types, the one that handles unsigned will also
// handle `Decimal`.
//
// That is, `ConvertFrom` for `int` will handle the other signed types and
// `ConvertTo` will handle the unsigned types
if (typeof(TOther) == typeof(double))
{
double actualValue = (double)(object)value;
result = (actualValue >= MaxValue) ? MaxValue :
(actualValue <= MinValue) ? MinValue : (int)actualValue;
return true;
}
else if (typeof(TOther) == typeof(Half))
{
Half actualValue = (Half)(object)value;
result = (actualValue == Half.PositiveInfinity) ? MaxValue :
(actualValue == Half.NegativeInfinity) ? MinValue : (int)actualValue;
return true;
}
else if (typeof(TOther) == typeof(short))
{
short actualValue = (short)(object)value;
result = actualValue;
return true;
}
else if (typeof(TOther) == typeof(long))
{
long actualValue = (long)(object)value;
result = (actualValue >= MaxValue) ? MaxValue :
(actualValue <= MinValue) ? MinValue : (int)actualValue;
return true;
}
else if (typeof(TOther) == typeof(Int128))
{
Int128 actualValue = (Int128)(object)value;
result = (actualValue >= MaxValue) ? MaxValue :
(actualValue <= MinValue) ? MinValue : (int)actualValue;
return true;
}
else if (typeof(TOther) == typeof(nint))
{
nint actualValue = (nint)(object)value;
result = (actualValue >= MaxValue) ? MaxValue :
(actualValue <= MinValue) ? MinValue : (int)actualValue;
return true;
}
else if (typeof(TOther) == typeof(sbyte))
{
sbyte actualValue = (sbyte)(object)value;
result = actualValue;
return true;
}
else if (typeof(TOther) == typeof(float))
{
float actualValue = (float)(object)value;
result = (actualValue >= MaxValue) ? MaxValue :
(actualValue <= MinValue) ? MinValue : (int)actualValue;
return true;
}
else
{
result = default;
return false;
}
}
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromTruncating{TOther}(TOther, out TSelf)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<int>.TryConvertFromTruncating<TOther>(TOther value, out int result) => TryConvertFromTruncating(value, out result);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryConvertFromTruncating<TOther>(TOther value, out int result)
where TOther : INumberBase<TOther>
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
// `ConvertTo` handle the opposite sign. However, since there is an uneven split
// between signed and unsigned types, the one that handles unsigned will also
// handle `Decimal`.
//
// That is, `ConvertFrom` for `int` will handle the other signed types and
// `ConvertTo` will handle the unsigned types
if (typeof(TOther) == typeof(double))
{
double actualValue = (double)(object)value;
result = (actualValue >= MaxValue) ? MaxValue :
(actualValue <= MinValue) ? MinValue : (int)actualValue;
return true;
}
else if (typeof(TOther) == typeof(Half))
{
Half actualValue = (Half)(object)value;
result = (actualValue == Half.PositiveInfinity) ? MaxValue :
(actualValue == Half.NegativeInfinity) ? MinValue : (int)actualValue;
return true;
}
else if (typeof(TOther) == typeof(short))
{
short actualValue = (short)(object)value;
result = actualValue;
return true;
}
else if (typeof(TOther) == typeof(long))
{
long actualValue = (long)(object)value;
result = (int)actualValue;
return true;
}
else if (typeof(TOther) == typeof(Int128))
{
Int128 actualValue = (Int128)(object)value;
result = (int)actualValue;
return true;
}
else if (typeof(TOther) == typeof(nint))
{
nint actualValue = (nint)(object)value;
result = (int)actualValue;
return true;
}
else if (typeof(TOther) == typeof(sbyte))
{
sbyte actualValue = (sbyte)(object)value;
result = actualValue;
return true;
}
else if (typeof(TOther) == typeof(float))
{
float actualValue = (float)(object)value;
result = (actualValue >= MaxValue) ? MaxValue :
(actualValue <= MinValue) ? MinValue : (int)actualValue;
return true;
}
else
{
result = default;
return false;
}
}
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<int>.TryConvertToChecked<TOther>(int value, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
// `ConvertTo` handle the opposite sign. However, since there is an uneven split
// between signed and unsigned types, the one that handles unsigned will also
// handle `Decimal`.
//
// That is, `ConvertFrom` for `int` will handle the other signed types and
// `ConvertTo` will handle the unsigned types
if (typeof(TOther) == typeof(byte))
{
byte actualResult = checked((byte)value);
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(char))
{
char actualResult = checked((char)value);
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(decimal))
{
decimal actualResult = value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(ushort))
{
ushort actualResult = checked((ushort)value);
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(uint))
{
uint actualResult = checked((uint)value);
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(ulong))
{
ulong actualResult = checked((ulong)value);
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(UInt128))
{
UInt128 actualResult = checked((UInt128)value);
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(nuint))
{
nuint actualResult = checked((nuint)value);
result = (TOther)(object)actualResult;
return true;
}
else
{
result = default;
return false;
}
}
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<int>.TryConvertToSaturating<TOther>(int value, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
// `ConvertTo` handle the opposite sign. However, since there is an uneven split
// between signed and unsigned types, the one that handles unsigned will also
// handle `Decimal`.
//
// That is, `ConvertFrom` for `int` will handle the other signed types and
// `ConvertTo` will handle the unsigned types
if (typeof(TOther) == typeof(byte))
{
byte actualResult = (value >= byte.MaxValue) ? byte.MaxValue :
(value <= byte.MinValue) ? byte.MinValue : (byte)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(char))
{
char actualResult = (value >= char.MaxValue) ? char.MaxValue :
(value <= char.MinValue) ? char.MinValue : (char)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(decimal))
{
decimal actualResult = value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(ushort))
{
ushort actualResult = (value >= ushort.MaxValue) ? ushort.MaxValue :
(value <= ushort.MinValue) ? ushort.MinValue : (ushort)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(uint))
{
uint actualResult = (value <= 0) ? uint.MinValue : (uint)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(ulong))
{
ulong actualResult = (value <= 0) ? ulong.MinValue : (ulong)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(UInt128))
{
UInt128 actualResult = (value <= 0) ? UInt128.MinValue : (UInt128)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(nuint))
{
nuint actualResult = (value <= 0) ? 0 : (nuint)value;
result = (TOther)(object)actualResult;
return true;
}
else
{
result = default;
return false;
}
}
/// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static bool INumberBase<int>.TryConvertToTruncating<TOther>(int value, [MaybeNullWhen(false)] out TOther result)
{
// In order to reduce overall code duplication and improve the inlinabilty of these
// methods for the corelib types we have `ConvertFrom` handle the same sign and
// `ConvertTo` handle the opposite sign. However, since there is an uneven split
// between signed and unsigned types, the one that handles unsigned will also
// handle `Decimal`.
//
// That is, `ConvertFrom` for `int` will handle the other signed types and
// `ConvertTo` will handle the unsigned types
if (typeof(TOther) == typeof(byte))
{
byte actualResult = (byte)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(char))
{
char actualResult = (char)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(decimal))
{
decimal actualResult = value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(ushort))
{
ushort actualResult = (ushort)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(uint))
{
uint actualResult = (uint)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(ulong))
{
ulong actualResult = (ulong)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(UInt128))
{
UInt128 actualResult = (UInt128)value;
result = (TOther)(object)actualResult;
return true;
}
else if (typeof(TOther) == typeof(nuint))
{
nuint actualResult = (nuint)value;
result = (TOther)(object)actualResult;
return true;
}
else
{
result = default;
return false;
}
}
//
// IParsable
//
/// <inheritdoc cref="IParsable{TSelf}.TryParse(string?, IFormatProvider?, out TSelf)" />
public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out int result) => TryParse(s, NumberStyles.Integer, provider, out result);
//
// IShiftOperators
//
/// <inheritdoc cref="IShiftOperators{TSelf, TOther, TResult}.op_LeftShift(TSelf, TOther)" />
static int IShiftOperators<int, int, int>.operator <<(int value, int shiftAmount) => value << shiftAmount;
/// <inheritdoc cref="IShiftOperators{TSelf, TOther, TResult}.op_RightShift(TSelf, TOther)" />
static int IShiftOperators<int, int, int>.operator >>(int value, int shiftAmount) => value >> shiftAmount;
/// <inheritdoc cref="IShiftOperators{TSelf, TOther, TResult}.op_UnsignedRightShift(TSelf, TOther)" />
static int IShiftOperators<int, int, int>.operator >>>(int value, int shiftAmount) => value >>> shiftAmount;
//
// ISignedNumber
//
/// <inheritdoc cref="ISignedNumber{TSelf}.NegativeOne" />
static int ISignedNumber<int>.NegativeOne => NegativeOne;
//
// ISpanParsable
//
/// <inheritdoc cref="ISpanParsable{TSelf}.Parse(ReadOnlySpan{char}, IFormatProvider?)" />
public static int Parse(ReadOnlySpan<char> s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider);
/// <inheritdoc cref="ISpanParsable{TSelf}.TryParse(ReadOnlySpan{char}, IFormatProvider?, out TSelf)" />
public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out int result) => TryParse(s, NumberStyles.Integer, provider, out result);
//
// ISubtractionOperators
//
/// <inheritdoc cref="ISubtractionOperators{TSelf, TOther, TResult}.op_Subtraction(TSelf, TOther)" />
static int ISubtractionOperators<int, int, int>.operator -(int left, int right) => left - right;
/// <inheritdoc cref="ISubtractionOperators{TSelf, TOther, TResult}.op_CheckedSubtraction(TSelf, TOther)" />
static int ISubtractionOperators<int, int, int>.operator checked -(int left, int right) => checked(left - right);
//
// IUnaryNegationOperators
//
/// <inheritdoc cref="IUnaryNegationOperators{TSelf, TResult}.op_UnaryNegation(TSelf)" />
static int IUnaryNegationOperators<int, int>.operator -(int value) => -value;
/// <inheritdoc cref="IUnaryNegationOperators{TSelf, TResult}.op_CheckedUnaryNegation(TSelf)" />
static int IUnaryNegationOperators<int, int>.operator checked -(int value) => checked(-value);
//
// IUnaryPlusOperators
//
/// <inheritdoc cref="IUnaryPlusOperators{TSelf, TResult}.op_UnaryPlus(TSelf)" />
static int IUnaryPlusOperators<int, int>.operator +(int value) => +value;
//
// IUtf8SpanParsable
//
/// <inheritdoc cref="INumberBase{TSelf}.Parse(ReadOnlySpan{byte}, NumberStyles, IFormatProvider?)" />
public static int Parse(ReadOnlySpan<byte> utf8Text, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
return Number.ParseBinaryInteger<byte, int>(utf8Text, style, NumberFormatInfo.GetInstance(provider));
}
/// <inheritdoc cref="INumberBase{TSelf}.TryParse(ReadOnlySpan{byte}, NumberStyles, IFormatProvider?, out TSelf)" />
public static bool TryParse(ReadOnlySpan<byte> utf8Text, NumberStyles style, IFormatProvider? provider, out int result)
{
NumberFormatInfo.ValidateParseStyleInteger(style);
return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
}
/// <inheritdoc cref="IUtf8SpanParsable{TSelf}.Parse(ReadOnlySpan{byte}, IFormatProvider?)" />
public static int Parse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider) => Parse(utf8Text, NumberStyles.Integer, provider);
/// <inheritdoc cref="IUtf8SpanParsable{TSelf}.TryParse(ReadOnlySpan{byte}, IFormatProvider?, out TSelf)" />
public static bool TryParse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider, out int result) => TryParse(utf8Text, NumberStyles.Integer, provider, out result);
//
// IBinaryIntegerParseAndFormatInfo
//
static bool IBinaryIntegerParseAndFormatInfo<int>.IsSigned => true;
static int IBinaryIntegerParseAndFormatInfo<int>.MaxDigitCount => 10; // 2_147_483_647
static int IBinaryIntegerParseAndFormatInfo<int>.MaxHexDigitCount => 8; // 0x7FFF_FFFF
static int IBinaryIntegerParseAndFormatInfo<int>.MaxValueDiv10 => MaxValue / 10;
static string IBinaryIntegerParseAndFormatInfo<int>.OverflowMessage => SR.Overflow_Int32;
static bool IBinaryIntegerParseAndFormatInfo<int>.IsGreaterThanAsUnsigned(int left, int right) => (uint)(left) > (uint)(right);
static int IBinaryIntegerParseAndFormatInfo<int>.MultiplyBy10(int value) => value * 10;
static int IBinaryIntegerParseAndFormatInfo<int>.MultiplyBy16(int value) => value * 16;
}
}
|