Single.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. /*============================================================
  5. **
  6. **
  7. **
  8. ** Purpose: A wrapper class for the primitive type float.
  9. **
  10. **
  11. ===========================================================*/
  12. using System.Globalization;
  13. using System.Runtime.CompilerServices;
  14. using System.Runtime.InteropServices;
  15. using System.Runtime.Versioning;
  16. using Internal.Runtime.CompilerServices;
  17. namespace System
  18. {
  19. [Serializable]
  20. [StructLayout(LayoutKind.Sequential)]
  21. [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
  22. public readonly struct Single : IComparable, IConvertible, IFormattable, IComparable<float>, IEquatable<float>, ISpanFormattable
  23. {
  24. private readonly float m_value; // Do not rename (binary serialization)
  25. //
  26. // Public constants
  27. //
  28. public const float MinValue = (float)-3.40282346638528859e+38;
  29. public const float Epsilon = (float)1.4e-45;
  30. public const float MaxValue = (float)3.40282346638528859e+38;
  31. public const float PositiveInfinity = (float)1.0 / (float)0.0;
  32. public const float NegativeInfinity = (float)-1.0 / (float)0.0;
  33. public const float NaN = (float)0.0 / (float)0.0;
  34. // We use this explicit definition to avoid the confusion between 0.0 and -0.0.
  35. internal const float NegativeZero = (float)-0.0;
  36. //
  37. // Constants for manipulating the private bit-representation
  38. //
  39. internal const uint SignMask = 0x8000_0000;
  40. internal const int SignShift = 31;
  41. internal const int ShiftedSignMask = (int)(SignMask >> SignShift);
  42. internal const uint ExponentMask = 0x7F80_0000;
  43. internal const int ExponentShift = 23;
  44. internal const int ShiftedExponentMask = (int)(ExponentMask >> ExponentShift);
  45. internal const uint SignificandMask = 0x007F_FFFF;
  46. internal const byte MinSign = 0;
  47. internal const byte MaxSign = 1;
  48. internal const byte MinExponent = 0x00;
  49. internal const byte MaxExponent = 0xFF;
  50. internal const uint MinSignificand = 0x0000_0000;
  51. internal const uint MaxSignificand = 0x007F_FFFF;
  52. /// <summary>Determines whether the specified value is finite (zero, subnormal, or normal).</summary>
  53. [NonVersionable]
  54. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  55. public static bool IsFinite(float f)
  56. {
  57. int bits = BitConverter.SingleToInt32Bits(f);
  58. return (bits & 0x7FFFFFFF) < 0x7F800000;
  59. }
  60. /// <summary>Determines whether the specified value is infinite.</summary>
  61. [NonVersionable]
  62. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  63. public static unsafe bool IsInfinity(float f)
  64. {
  65. int bits = BitConverter.SingleToInt32Bits(f);
  66. return (bits & 0x7FFFFFFF) == 0x7F800000;
  67. }
  68. /// <summary>Determines whether the specified value is NaN.</summary>
  69. [NonVersionable]
  70. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  71. public static unsafe bool IsNaN(float f)
  72. {
  73. // A NaN will never equal itself so this is an
  74. // easy and efficient way to check for NaN.
  75. #pragma warning disable CS1718
  76. return f != f;
  77. #pragma warning restore CS1718
  78. }
  79. /// <summary>Determines whether the specified value is negative.</summary>
  80. [NonVersionable]
  81. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  82. public static unsafe bool IsNegative(float f)
  83. {
  84. return BitConverter.SingleToInt32Bits(f) < 0;
  85. }
  86. /// <summary>Determines whether the specified value is negative infinity.</summary>
  87. [NonVersionable]
  88. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  89. public static unsafe bool IsNegativeInfinity(float f)
  90. {
  91. return f == float.NegativeInfinity;
  92. }
  93. /// <summary>Determines whether the specified value is normal.</summary>
  94. [NonVersionable]
  95. // This is probably not worth inlining, it has branches and should be rarely called
  96. public static unsafe bool IsNormal(float f)
  97. {
  98. int bits = BitConverter.SingleToInt32Bits(f);
  99. bits &= 0x7FFFFFFF;
  100. return (bits < 0x7F800000) && (bits != 0) && ((bits & 0x7F800000) != 0);
  101. }
  102. /// <summary>Determines whether the specified value is positive infinity.</summary>
  103. [NonVersionable]
  104. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  105. public static unsafe bool IsPositiveInfinity(float f)
  106. {
  107. return f == float.PositiveInfinity;
  108. }
  109. /// <summary>Determines whether the specified value is subnormal.</summary>
  110. [NonVersionable]
  111. // This is probably not worth inlining, it has branches and should be rarely called
  112. public static unsafe bool IsSubnormal(float f)
  113. {
  114. int bits = BitConverter.SingleToInt32Bits(f);
  115. bits &= 0x7FFFFFFF;
  116. return (bits < 0x7F800000) && (bits != 0) && ((bits & 0x7F800000) == 0);
  117. }
  118. internal static int ExtractExponentFromBits(uint bits)
  119. {
  120. return (int)(bits >> ExponentShift) & ShiftedExponentMask;
  121. }
  122. internal static uint ExtractSignificandFromBits(uint bits)
  123. {
  124. return bits & SignificandMask;
  125. }
  126. // Compares this object to another object, returning an integer that
  127. // indicates the relationship.
  128. // Returns a value less than zero if this object
  129. // null is considered to be less than any instance.
  130. // If object is not of type Single, this method throws an ArgumentException.
  131. //
  132. public int CompareTo(object? value)
  133. {
  134. if (value == null)
  135. {
  136. return 1;
  137. }
  138. if (value is float f)
  139. {
  140. if (m_value < f) return -1;
  141. if (m_value > f) return 1;
  142. if (m_value == f) return 0;
  143. // At least one of the values is NaN.
  144. if (IsNaN(m_value))
  145. return IsNaN(f) ? 0 : -1;
  146. else // f is NaN.
  147. return 1;
  148. }
  149. throw new ArgumentException(SR.Arg_MustBeSingle);
  150. }
  151. public int CompareTo(float value)
  152. {
  153. if (m_value < value) return -1;
  154. if (m_value > value) return 1;
  155. if (m_value == value) return 0;
  156. // At least one of the values is NaN.
  157. if (IsNaN(m_value))
  158. return IsNaN(value) ? 0 : -1;
  159. else // f is NaN.
  160. return 1;
  161. }
  162. [NonVersionable]
  163. public static bool operator ==(float left, float right) => left == right;
  164. [NonVersionable]
  165. public static bool operator !=(float left, float right) => left != right;
  166. [NonVersionable]
  167. public static bool operator <(float left, float right) => left < right;
  168. [NonVersionable]
  169. public static bool operator >(float left, float right) => left > right;
  170. [NonVersionable]
  171. public static bool operator <=(float left, float right) => left <= right;
  172. [NonVersionable]
  173. public static bool operator >=(float left, float right) => left >= right;
  174. public override bool Equals(object? obj)
  175. {
  176. if (!(obj is float))
  177. {
  178. return false;
  179. }
  180. float temp = ((float)obj).m_value;
  181. if (temp == m_value)
  182. {
  183. return true;
  184. }
  185. return IsNaN(temp) && IsNaN(m_value);
  186. }
  187. public bool Equals(float obj)
  188. {
  189. if (obj == m_value)
  190. {
  191. return true;
  192. }
  193. return IsNaN(obj) && IsNaN(m_value);
  194. }
  195. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  196. public override int GetHashCode()
  197. {
  198. int bits = Unsafe.As<float, int>(ref Unsafe.AsRef(in m_value));
  199. // Optimized check for IsNan() || IsZero()
  200. if (((bits - 1) & 0x7FFFFFFF) >= 0x7F800000)
  201. {
  202. // Ensure that all NaNs and both zeros have the same hash code
  203. bits &= 0x7F800000;
  204. }
  205. return bits;
  206. }
  207. public override string ToString()
  208. {
  209. return Number.FormatSingle(m_value, null, NumberFormatInfo.CurrentInfo);
  210. }
  211. public string ToString(IFormatProvider? provider)
  212. {
  213. return Number.FormatSingle(m_value, null, NumberFormatInfo.GetInstance(provider));
  214. }
  215. public string ToString(string? format)
  216. {
  217. return Number.FormatSingle(m_value, format, NumberFormatInfo.CurrentInfo);
  218. }
  219. public string ToString(string? format, IFormatProvider? provider)
  220. {
  221. return Number.FormatSingle(m_value, format, NumberFormatInfo.GetInstance(provider));
  222. }
  223. public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
  224. {
  225. return Number.TryFormatSingle(m_value, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten);
  226. }
  227. // Parses a float from a String in the given style. If
  228. // a NumberFormatInfo isn't specified, the current culture's
  229. // NumberFormatInfo is assumed.
  230. //
  231. // This method will not throw an OverflowException, but will return
  232. // PositiveInfinity or NegativeInfinity for a number that is too
  233. // large or too small.
  234. //
  235. public static float Parse(string s)
  236. {
  237. if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
  238. return Number.ParseSingle(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo);
  239. }
  240. public static float Parse(string s, NumberStyles style)
  241. {
  242. NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
  243. if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
  244. return Number.ParseSingle(s, style, NumberFormatInfo.CurrentInfo);
  245. }
  246. public static float Parse(string s, IFormatProvider? provider)
  247. {
  248. if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
  249. return Number.ParseSingle(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.GetInstance(provider));
  250. }
  251. public static float Parse(string s, NumberStyles style, IFormatProvider? provider)
  252. {
  253. NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
  254. if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s);
  255. return Number.ParseSingle(s, style, NumberFormatInfo.GetInstance(provider));
  256. }
  257. public static float Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Float | NumberStyles.AllowThousands, IFormatProvider? provider = null)
  258. {
  259. NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
  260. return Number.ParseSingle(s, style, NumberFormatInfo.GetInstance(provider));
  261. }
  262. public static bool TryParse(string? s, out float result)
  263. {
  264. if (s == null)
  265. {
  266. result = 0;
  267. return false;
  268. }
  269. return TryParse((ReadOnlySpan<char>)s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out result);
  270. }
  271. public static bool TryParse(ReadOnlySpan<char> s, out float result)
  272. {
  273. return TryParse(s, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out result);
  274. }
  275. public static bool TryParse(string? s, NumberStyles style, IFormatProvider? provider, out float result)
  276. {
  277. NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
  278. if (s == null)
  279. {
  280. result = 0;
  281. return false;
  282. }
  283. return TryParse((ReadOnlySpan<char>)s, style, NumberFormatInfo.GetInstance(provider), out result);
  284. }
  285. public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out float result)
  286. {
  287. NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
  288. return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result);
  289. }
  290. private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out float result)
  291. {
  292. return Number.TryParseSingle(s, style, info, out result);
  293. }
  294. //
  295. // IConvertible implementation
  296. //
  297. public TypeCode GetTypeCode()
  298. {
  299. return TypeCode.Single;
  300. }
  301. bool IConvertible.ToBoolean(IFormatProvider? provider)
  302. {
  303. return Convert.ToBoolean(m_value);
  304. }
  305. char IConvertible.ToChar(IFormatProvider? provider)
  306. {
  307. throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Single", "Char"));
  308. }
  309. sbyte IConvertible.ToSByte(IFormatProvider? provider)
  310. {
  311. return Convert.ToSByte(m_value);
  312. }
  313. byte IConvertible.ToByte(IFormatProvider? provider)
  314. {
  315. return Convert.ToByte(m_value);
  316. }
  317. short IConvertible.ToInt16(IFormatProvider? provider)
  318. {
  319. return Convert.ToInt16(m_value);
  320. }
  321. ushort IConvertible.ToUInt16(IFormatProvider? provider)
  322. {
  323. return Convert.ToUInt16(m_value);
  324. }
  325. int IConvertible.ToInt32(IFormatProvider? provider)
  326. {
  327. return Convert.ToInt32(m_value);
  328. }
  329. uint IConvertible.ToUInt32(IFormatProvider? provider)
  330. {
  331. return Convert.ToUInt32(m_value);
  332. }
  333. long IConvertible.ToInt64(IFormatProvider? provider)
  334. {
  335. return Convert.ToInt64(m_value);
  336. }
  337. ulong IConvertible.ToUInt64(IFormatProvider? provider)
  338. {
  339. return Convert.ToUInt64(m_value);
  340. }
  341. float IConvertible.ToSingle(IFormatProvider? provider)
  342. {
  343. return m_value;
  344. }
  345. double IConvertible.ToDouble(IFormatProvider? provider)
  346. {
  347. return Convert.ToDouble(m_value);
  348. }
  349. decimal IConvertible.ToDecimal(IFormatProvider? provider)
  350. {
  351. return Convert.ToDecimal(m_value);
  352. }
  353. DateTime IConvertible.ToDateTime(IFormatProvider? provider)
  354. {
  355. throw new InvalidCastException(SR.Format(SR.InvalidCast_FromTo, "Single", "DateTime"));
  356. }
  357. object IConvertible.ToType(Type type, IFormatProvider? provider)
  358. {
  359. return Convert.DefaultToType((IConvertible)this, type, provider);
  360. }
  361. }
  362. }