| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986 |
- /* Copyright 2016 MongoDB Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Text.RegularExpressions;
- namespace MongoDB.Bson
- {
- /// <summary>
- /// Represents a Decimal128 value.
- /// </summary>
- #if NET45
- [Serializable]
- #endif
- public struct Decimal128 : IConvertible, IComparable<Decimal128>, IEquatable<Decimal128>
- {
- #region static
- // private constants
- private const short __exponentMax = 6111;
- private const short __exponentMin = -6176;
- private const short __exponentBias = 6176;
- private const short __maxSignificandDigits = 34;
- // private static fields
- private static readonly UInt128 __maxSignificand = UInt128.Parse("9999999999999999999999999999999999");
- private static readonly Decimal128 __maxValue = Decimal128.Parse("9999999999999999999999999999999999E+6111");
- private static readonly Decimal128 __minValue = Decimal128.Parse("-9999999999999999999999999999999999E+6111");
- // public static properties
- /// <summary>
- /// Gets the maximum value.
- /// </summary>
- public static Decimal128 MaxValue =>
- __maxValue;
- /// <summary>
- /// Gets the minimum value.
- /// </summary>
- public static Decimal128 MinValue =>
- __minValue;
- /// <summary>
- /// Represents negative infinity.
- /// </summary>
- public static Decimal128 NegativeInfinity =>
- new Decimal128(Flags.NegativeInfinity, 0);
- /// <summary>
- /// Represents one.
- /// </summary>
- public static Decimal128 One =>
- new Decimal128(0, 1);
- /// <summary>
- /// Represents positive infinity.
- /// </summary>
- public static Decimal128 PositiveInfinity =>
- new Decimal128(Flags.PositiveInfinity, 0);
- /// <summary>
- /// Represents a value that is not a number.
- /// </summary>
- public static Decimal128 QNaN =>
- new Decimal128(Flags.QNaN, 0);
- /// <summary>
- /// Represents a value that is not a number and raises errors when used in calculations.
- /// </summary>
- public static Decimal128 SNaN =>
- new Decimal128(Flags.SNaN, 0);
- /// <summary>
- /// Represents zero.
- /// </summary>
- public static Decimal128 Zero =>
- new Decimal128(0, 0);
- // public static operators
- /// <summary>
- /// Implements the operator ==.
- /// </summary>
- /// <param name="lhs">The LHS.</param>
- /// <param name="rhs">The RHS.</param>
- /// <returns>
- /// The result of the operator.
- /// </returns>
- public static bool operator ==(Decimal128 lhs, Decimal128 rhs)
- {
- if (Decimal128.IsNaN(lhs) || Decimal128.IsNaN(rhs))
- {
- return false;
- }
- else
- {
- return lhs.Equals(rhs);
- }
- }
- /// <summary>
- /// Implements the operator !=.
- /// </summary>
- /// <param name="lhs">The LHS.</param>
- /// <param name="rhs">The RHS.</param>
- /// <returns>
- /// The result of the operator.
- /// </returns>
- public static bool operator !=(Decimal128 lhs, Decimal128 rhs)
- {
- return !(lhs == rhs);
- }
- /// <summary>
- /// Returns a value indicating whether a specified Decimal128 is greater than another specified Decimal128.
- /// </summary>
- /// <param name="x">The first value.</param>
- /// <param name="y">The second value.</param>
- /// <returns>
- /// true if x > y; otherwise, false.
- /// </returns>
- public static bool operator >(Decimal128 x, Decimal128 y)
- {
- return Decimal128.Compare(x, y) > 0;
- }
- /// <summary>
- /// Returns a value indicating whether a specified Decimal128 is greater than or equal to another another specified Decimal128.
- /// </summary>
- /// <param name="x">The first value.</param>
- /// <param name="y">The second value.</param>
- /// <returns>
- /// true if x >= y; otherwise, false.
- /// </returns>
- public static bool operator >=(Decimal128 x, Decimal128 y)
- {
- return Decimal128.Compare(x, y) >= 0;
- }
- /// <summary>
- /// Returns a value indicating whether a specified Decimal128 is less than another specified Decimal128.
- /// </summary>
- /// <param name="x">The first value.</param>
- /// <param name="y">The second value.</param>
- /// <returns>
- /// true if x < y; otherwise, false.
- /// </returns>
- public static bool operator <(Decimal128 x, Decimal128 y)
- {
- return Decimal128.Compare(x, y) < 0;
- }
- /// <summary>
- /// Returns a value indicating whether a specified Decimal128 is less than or equal to another another specified Decimal128.
- /// </summary>
- /// <param name="x">The first value.</param>
- /// <param name="y">The second value.</param>
- /// <returns>
- /// true if x <= y; otherwise, false.
- /// </returns>
- public static bool operator <=(Decimal128 x, Decimal128 y)
- {
- return Decimal128.Compare(x, y) <= 0;
- }
- /// <summary>
- /// Performs an explicit conversion from <see cref="Decimal128"/> to <see cref="System.Byte"/>.
- /// </summary>
- /// <param name="value">The value to convert.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- public static explicit operator byte(Decimal128 value)
- {
- return ToByte(value);
- }
- /// <summary>
- /// Performs an explicit conversion from <see cref="Decimal128"/> to <see cref="char"/>.
- /// </summary>
- /// <param name="value">The value to convert.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- public static explicit operator char(Decimal128 value)
- {
- return (char)ToUInt16(value);
- }
- /// <summary>
- /// Performs an explicit conversion from <see cref="Decimal128"/> to <see cref="System.Decimal"/>.
- /// </summary>
- /// <param name="value">The value to convert.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- public static explicit operator decimal(Decimal128 value)
- {
- return ToDecimal(value);
- }
- /// <summary>
- /// Performs an implicit conversion from <see cref="System.Byte"/> to <see cref="Decimal128"/>.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- public static implicit operator Decimal128(byte value)
- {
- return new Decimal128(value);
- }
- /// <summary>
- /// Performs an implicit conversion from <see cref="System.Decimal"/> to <see cref="Decimal128"/>.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- public static implicit operator Decimal128(decimal value)
- {
- return new Decimal128(value);
- }
- /// <summary>
- /// Performs an explicit conversion from <see cref="double"/> to <see cref="Decimal128"/>.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- public static explicit operator Decimal128(double value)
- {
- return new Decimal128(value);
- }
- /// <summary>
- /// Performs an explicit conversion from <see cref="float"/> to <see cref="Decimal128"/>.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- public static explicit operator Decimal128(float value)
- {
- return new Decimal128(value);
- }
- /// <summary>
- /// Performs an implicit conversion from <see cref="System.Int32"/> to <see cref="Decimal128"/>.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- public static implicit operator Decimal128(int value)
- {
- return new Decimal128(value);
- }
- /// <summary>
- /// Performs an implicit conversion from <see cref="System.Int64"/> to <see cref="Decimal128"/>.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- public static implicit operator Decimal128(long value)
- {
- return new Decimal128(value);
- }
- /// <summary>
- /// Performs an implicit conversion from <see cref="System.SByte"/> to <see cref="Decimal128"/>.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- [CLSCompliant(false)]
- public static implicit operator Decimal128(sbyte value)
- {
- return new Decimal128(value);
- }
- /// <summary>
- /// Performs an implicit conversion from <see cref="System.Int16"/> to <see cref="Decimal128"/>.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- public static implicit operator Decimal128(short value)
- {
- return new Decimal128(value);
- }
- /// <summary>
- /// Performs an implicit conversion from <see cref="System.UInt32"/> to <see cref="Decimal128"/>.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- [CLSCompliant(false)]
- public static implicit operator Decimal128(uint value)
- {
- return new Decimal128(value);
- }
- /// <summary>
- /// Performs an implicit conversion from <see cref="System.UInt16"/> to <see cref="Decimal128"/>.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- [CLSCompliant(false)]
- public static implicit operator Decimal128(ushort value)
- {
- return new Decimal128(value);
- }
- /// <summary>
- /// Performs an implicit conversion from <see cref="System.UInt64"/> to <see cref="Decimal128"/>.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- [CLSCompliant(false)]
- public static implicit operator Decimal128(ulong value)
- {
- return new Decimal128(value);
- }
- /// <summary>
- /// Performs an explicit conversion from <see cref="Decimal128"/> to <see cref="double"/>.
- /// </summary>
- /// <param name="value">The value to convert.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- public static explicit operator double(Decimal128 value)
- {
- return Decimal128.ToDouble(value);
- }
- /// <summary>
- /// Performs an explicit conversion from <see cref="Decimal128"/> to <see cref="float"/>.
- /// </summary>
- /// <param name="value">The value to convert.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- public static explicit operator float(Decimal128 value)
- {
- return Decimal128.ToSingle(value);
- }
- /// <summary>
- /// Performs an explicit conversion from <see cref="Decimal128"/> to <see cref="System.Int32"/>.
- /// </summary>
- /// <param name="value">The value to convert.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- public static explicit operator int(Decimal128 value)
- {
- return ToInt32(value);
- }
- /// <summary>
- /// Performs an explicit conversion from <see cref="Decimal128"/> to <see cref="System.Int64"/>.
- /// </summary>
- /// <param name="value">The value to convert.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- public static explicit operator long(Decimal128 value)
- {
- return ToInt64(value);
- }
- /// <summary>
- /// Performs an explicit conversion from <see cref="Decimal128"/> to <see cref="System.SByte"/>.
- /// </summary>
- /// <param name="value">The value to convert.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- [CLSCompliant(false)]
- public static explicit operator sbyte(Decimal128 value)
- {
- return ToSByte(value);
- }
- /// <summary>
- /// Performs an explicit conversion from <see cref="Decimal128"/> to <see cref="System.Int16"/>.
- /// </summary>
- /// <param name="value">The value to convert.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- public static explicit operator short(Decimal128 value)
- {
- return ToInt16(value);
- }
- /// <summary>
- /// Performs an explicit conversion from <see cref="Decimal128"/> to <see cref="System.UInt32"/>.
- /// </summary>
- /// <param name="value">The value to convert.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- [CLSCompliant(false)]
- public static explicit operator uint(Decimal128 value)
- {
- return ToUInt32(value);
- }
- /// <summary>
- /// Performs an explicit conversion from <see cref="Decimal128"/> to <see cref="System.UInt64"/>.
- /// </summary>
- /// <param name="value">The value to convert.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- [CLSCompliant(false)]
- public static explicit operator ulong(Decimal128 value)
- {
- return ToUInt64(value);
- }
- /// <summary>
- /// Performs an explicit conversion from <see cref="Decimal128"/> to <see cref="System.UInt16"/>.
- /// </summary>
- /// <param name="value">The value to convert.</param>
- /// <returns>
- /// The result of the conversion.
- /// </returns>
- [CLSCompliant(false)]
- public static explicit operator ushort(Decimal128 value)
- {
- return ToUInt16(value);
- }
- // public static methods
- /// <summary>
- /// Compares two specified Decimal128 values and returns an integer that indicates whether the first value
- /// is greater than, less than, or equal to the second value.
- /// </summary>
- /// <param name="x">The first value.</param>
- /// <param name="y">The second value.</param>
- /// <returns>Less than zero if x < y, zero if x == y, and greater than zero if x > y.</returns>
- public static int Compare(Decimal128 x, Decimal128 y)
- {
- return Decimal128Comparer.Instance.Compare(x, y);
- }
- /// <summary>
- /// Determines whether the specified Decimal128 instances are considered equal.
- /// </summary>
- /// <param name="x">The first Decimal128 object to compare.</param>
- /// <param name="y">The second Decimal128 object to compare.</param>
- /// <returns>True if the objects are considered equal; otherwise false. If both x and y are null, the method returns true.</returns>
- public static bool Equals(Decimal128 x, Decimal128 y)
- {
- return Decimal128.Compare(x, y) == 0;
- }
- /// <summary>
- /// Creates a new Decimal128 value from its components.
- /// </summary>
- /// <param name="isNegative">if set to <c>true</c> [is negative].</param>
- /// <param name="exponent">The exponent.</param>
- /// <param name="significandHighBits">The signficand high bits.</param>
- /// <param name="significandLowBits">The significand low bits.</param>
- /// <returns>A Decimal128 value.</returns>
- [CLSCompliant(false)]
- public static Decimal128 FromComponents(bool isNegative, short exponent, ulong significandHighBits, ulong significandLowBits)
- {
- return FromComponents(isNegative, exponent, new UInt128(significandHighBits, significandLowBits));
- }
- /// <summary>
- /// Creates a new Decimal128 value from the IEEE encoding bits.
- /// </summary>
- /// <param name="highBits">The high bits.</param>
- /// <param name="lowBits">The low bits.</param>
- /// <returns>A Decimal128 value.</returns>
- [CLSCompliant(false)]
- public static Decimal128 FromIEEEBits(ulong highBits, ulong lowBits)
- {
- return new Decimal128(MapIEEEHighBitsToDecimal128HighBits(highBits), lowBits);
- }
- /// <summary>
- /// Gets the exponent of a Decimal128 value.
- /// </summary>
- /// <param name="d">The Decimal128 value.</param>
- /// <returns>The exponent.</returns>
- public static short GetExponent(Decimal128 d)
- {
- if (Flags.IsFirstForm(d._highBits))
- {
- return MapDecimal128BiasedExponentToExponent((short)((d._highBits & Flags.FirstFormExponentBits) >> 49));
- }
- else if (Flags.IsSecondForm(d._highBits))
- {
- return MapDecimal128BiasedExponentToExponent((short)((d._highBits & Flags.SecondFormExponentBits) >> 47));
- }
- else
- {
- throw new InvalidOperationException("GetExponent cannot be called for Infinity or NaN.");
- }
- }
- /// <summary>
- /// Gets the high bits of the significand of a Decimal128 value.
- /// </summary>
- /// <param name="d">The Decimal128 value.</param>
- /// <returns>The high bits of the significand.</returns>
- [CLSCompliant(false)]
- public static ulong GetSignificandHighBits(Decimal128 d)
- {
- if (Flags.IsFirstForm(d._highBits))
- {
- return d._highBits & Flags.FirstFormSignificandBits;
- }
- else if (Flags.IsSecondForm(d._highBits))
- {
- return 0;
- }
- else
- {
- throw new InvalidOperationException("GetSignificandHighBits cannot be called for Infinity or NaN.");
- }
- }
- /// <summary>
- /// Gets the high bits of the significand of a Decimal128 value.
- /// </summary>
- /// <param name="d">The Decimal128 value.</param>
- /// <returns>The high bits of the significand.</returns>
- [CLSCompliant(false)]
- public static ulong GetSignificandLowBits(Decimal128 d)
- {
- if (Flags.IsFirstForm(d._highBits))
- {
- return d._lowBits;
- }
- else if (Flags.IsSecondForm(d._highBits))
- {
- return 0;
- }
- else
- {
- throw new InvalidOperationException("GetSignificandLowBits cannot be called for Infinity or NaN.");
- }
- }
- /// <summary>
- /// Returns a value indicating whether the specified number evaluates to negative or positive infinity.
- /// </summary>
- /// <param name="d">A 128-bit decimal.</param>
- /// <returns>true if <paramref name="d" /> evaluates to negative or positive infinity; otherwise, false.</returns>
- public static bool IsInfinity(Decimal128 d) => Flags.IsInfinity(d._highBits);
- /// <summary>
- /// Returns a value indicating whether the specified number is not a number.
- /// </summary>
- /// <param name="d">A 128-bit decimal.</param>
- /// <returns>true if <paramref name="d" /> is not a number; otherwise, false.</returns>
- public static bool IsNaN(Decimal128 d) => Flags.IsNaN(d._highBits);
- /// <summary>
- /// Returns a value indicating whether the specified number is negative.
- /// </summary>
- /// <param name="d">A 128-bit decimal.</param>
- /// <returns>true if <paramref name="d" /> is negative; otherwise, false.</returns>
- public static bool IsNegative(Decimal128 d) => Flags.IsNegative(d._highBits);
- /// <summary>
- /// Returns a value indicating whether the specified number evaluates to negative infinity.
- /// </summary>
- /// <param name="d">A 128-bit decimal.</param>
- /// <returns>true if <paramref name="d" /> evaluates to negative infinity; otherwise, false.</returns>
- public static bool IsNegativeInfinity(Decimal128 d) => Flags.IsNegativeInfinity(d._highBits);
- /// <summary>
- /// Returns a value indicating whether the specified number evaluates to positive infinity.
- /// </summary>
- /// <param name="d">A 128-bit decimal.</param>
- /// <returns>true if <paramref name="d" /> evaluates to positive infinity; otherwise, false.</returns>
- public static bool IsPositiveInfinity(Decimal128 d) => Flags.IsPositiveInfinity(d._highBits);
- /// <summary>
- /// Returns a value indicating whether the specified number is a quiet not a number.
- /// </summary>
- /// <param name="d">A 128-bit decimal.</param>
- /// <returns>true if <paramref name="d" /> is a quiet not a number; otherwise, false.</returns>
- public static bool IsQNaN(Decimal128 d) => Flags.IsQNaN(d._highBits);
- /// <summary>
- /// Returns a value indicating whether the specified number is a signaled not a number.
- /// </summary>
- /// <param name="d">A 128-bit decimal.</param>
- /// <returns>true if <paramref name="d" /> is a signaled not a number; otherwise, false.</returns>
- public static bool IsSNaN(Decimal128 d) => Flags.IsSNaN(d._highBits);
- /// <summary>
- /// Gets a value indicating whether this instance is zero.
- /// </summary>
- /// <value>
- /// <c>true</c> if this instance is zero; otherwise, <c>false</c>.
- /// </value>
- public static bool IsZero(Decimal128 d)
- {
- if (Flags.IsFirstForm(d._highBits) && GetSignificand(d).Equals(UInt128.Zero))
- {
- return true;
- }
- else if (Flags.IsSecondForm(d._highBits))
- {
- // all second form values are invalid representations and are interpreted as zero
- return true;
- }
- else
- {
- return false;
- }
- }
- /// <summary>
- /// Negates the specified x.
- /// </summary>
- /// <param name="x">The x.</param>
- /// <returns>The result of multiplying the value by negative one.</returns>
- public static Decimal128 Negate(Decimal128 x)
- {
- return new Decimal128(x._highBits ^ Flags.SignBit, x._lowBits);
- }
- /// <summary>
- /// Converts the string representation of a number to its <see cref="Decimal128" /> equivalent.
- /// </summary>
- /// <param name="s">The string representation of the number to convert.</param>
- /// <returns>
- /// The equivalent to the number contained in <paramref name="s" />.
- /// </returns>
- public static Decimal128 Parse(string s)
- {
- Decimal128 value;
- if (!TryParse(s, out value))
- {
- throw new FormatException($"{s} is not a valid Decimal128.");
- }
- return value;
- }
- /// <summary>
- /// Converts the value of the specified <see cref="Decimal128"/> to the equivalent 8-bit unsigned integer.
- /// </summary>
- /// <param name="d">The number to convert.</param>
- /// <returns>A 8-bit unsigned integer equivalent to <paramref name="d" />.</returns>
- public static byte ToByte(Decimal128 d)
- {
- if (Flags.IsFirstForm(d._highBits))
- {
- ulong value;
- if (Decimal128.TryTruncateToUInt64(d, 0, byte.MaxValue, out value))
- {
- return (byte)value;
- }
- else
- {
- throw new OverflowException("Value is too large or too small to be converted to a Byte.");
- }
- }
- else if (Flags.IsSecondForm(d._highBits))
- {
- return 0;
- }
- else
- {
- throw new OverflowException("Infinity or NaN cannot be converted to a Byte.");
- }
- }
- /// <summary>
- /// Converts the value of the specified <see cref="Decimal128"/> to the equivalent <see cref="decimal"/>.
- /// </summary>
- /// <param name="d">The number to convert.</param>
- /// <returns>A <see cref="decimal"/> equivalent to <paramref name="d" />.</returns>
- public static decimal ToDecimal(Decimal128 d)
- {
- if (Flags.IsFirstForm(d._highBits))
- {
- var exponent = Decimal128.GetExponent(d);
- // try to get the exponent within the range of 0 to -28
- if (exponent > 0)
- {
- d = Decimal128.DecreaseExponent(d, 0);
- exponent = Decimal128.GetExponent(d);
- }
- else if (exponent < -28)
- {
- d = Decimal128.IncreaseExponent(d, -28);
- exponent = Decimal128.GetExponent(d);
- }
- // try to get the significand to have zeros for the high order 32 bits
- var significand = Decimal128.GetSignificand(d);
- while ((significand.High >> 32) != 0)
- {
- uint remainder;
- var significandDividedBy10 = UInt128.Divide(significand, (uint)10, out remainder);
- if (remainder != 0)
- {
- break;
- }
- exponent += 1;
- significand = significandDividedBy10;
- }
- if (exponent < -28 || exponent > 0 || (significand.High >> 32) != 0)
- {
- throw new OverflowException("Value is too large or too small to be converted to a Decimal.");
- }
- var lo = (int)significand.Low;
- var mid = (int)(significand.Low >> 32);
- var hi = (int)significand.High;
- var isNegative = Decimal128.IsNegative(d);
- var scale = (byte)(-exponent);
- return new decimal(lo, mid, hi, isNegative, scale);
- }
- else if (Flags.IsSecondForm(d._highBits))
- {
- return Decimal.Zero;
- }
- else
- {
- throw new OverflowException("Infinity or NaN cannot be converted to Decimal.");
- }
- }
- /// <summary>
- /// Converts the value of the specified <see cref="Decimal128"/> to the equivalent <see cref="double"/>.
- /// </summary>
- /// <param name="d">The number to convert.</param>
- /// <returns>A <see cref="double"/> equivalent to <paramref name="d" />.</returns>
- public static double ToDouble(Decimal128 d)
- {
- if (Flags.IsFirstForm(d._highBits))
- {
- // TODO: implement this more efficiently
- var stringValue = d.ToString();
- return double.Parse(stringValue);
- }
- else if (Flags.IsSecondForm(d._highBits))
- {
- return 0.0;
- }
- else if (Flags.IsPositiveInfinity(d._highBits))
- {
- return double.PositiveInfinity;
- }
- else if (Flags.IsNegativeInfinity(d._highBits))
- {
- return double.NegativeInfinity;
- }
- else
- {
- return double.NaN;
- }
- }
- /// <summary>
- /// Converts the value of the specified <see cref="Decimal128"/> to the equivalent 16-bit signed integer.
- /// </summary>
- /// <param name="d">The number to convert.</param>
- /// <returns>A 16-bit signed integer equivalent to <paramref name="d" />.</returns>
- public static short ToInt16(Decimal128 d)
- {
- if (Flags.IsFirstForm(d._highBits))
- {
- var maxNegativeValue = (ulong)short.MaxValue + 1;
- ulong value;
- if (Decimal128.TryTruncateToUInt64(d, maxNegativeValue, (ulong)short.MaxValue, out value))
- {
- return Decimal128.IsNegative(d) ? (value == maxNegativeValue ? short.MinValue : (short )(-(short)value)) : (short)value;
- }
- else
- {
- throw new OverflowException("Value is too large or too small to be converted to an Int16.");
- }
- }
- else if (Flags.IsSecondForm(d._highBits))
- {
- return 0;
- }
- else
- {
- throw new OverflowException("Infinity or NaN cannot be converted to an Int16.");
- }
- }
- /// <summary>
- /// Converts the value of the specified <see cref="Decimal128"/> to the equivalent 32-bit signed integer.
- /// </summary>
- /// <param name="d">The number to convert.</param>
- /// <returns>A 32-bit signed integer equivalent to <paramref name="d" />.</returns>
- public static int ToInt32(Decimal128 d)
- {
- if (Flags.IsFirstForm(d._highBits))
- {
- var maxNegativeValue = (ulong)int.MaxValue + 1;
- ulong value;
- if (Decimal128.TryTruncateToUInt64(d, maxNegativeValue, int.MaxValue, out value))
- {
- return Decimal128.IsNegative(d) ? (value == maxNegativeValue ? int.MinValue : -(int)value) : (int)value;
- }
- else
- {
- throw new OverflowException("Value is too large or too small to be converted to an Int32.");
- }
- }
- else if (Flags.IsSecondForm(d._highBits))
- {
- return 0;
- }
- else
- {
- throw new OverflowException("Infinity or NaN cannot be converted to an Int32.");
- }
- }
- /// <summary>
- /// Converts the value of the specified <see cref="Decimal128"/> to the equivalent 64-bit signed integer.
- /// </summary>
- /// <param name="d">The number to convert.</param>
- /// <returns>A 64-bit signed integer equivalent to <paramref name="d" />.</returns>
- public static long ToInt64(Decimal128 d)
- {
- if (Flags.IsFirstForm(d._highBits))
- {
- ulong maxNegativeValue = (ulong)long.MaxValue + 1;
- ulong value;
- if (Decimal128.TryTruncateToUInt64(d, maxNegativeValue, long.MaxValue, out value))
- {
- return Decimal128.IsNegative(d) ? (value == maxNegativeValue ? long.MinValue : -(long)value) : (long)value;
- }
- else
- {
- throw new OverflowException("Value is too large or too small to be converted to an Int64.");
- }
- }
- else if (Flags.IsSecondForm(d._highBits))
- {
- return 0;
- }
- else
- {
- throw new OverflowException("Infinity or NaN cannot be converted to an Int64.");
- }
- }
- /// <summary>
- /// Converts the value of the specified <see cref="Decimal128"/> to the equivalent 8-bit signed integer.
- /// </summary>
- /// <param name="d">The number to convert.</param>
- /// <returns>A 8-bit signed integer equivalent to <paramref name="d" />.</returns>
- [CLSCompliant(false)]
- public static sbyte ToSByte(Decimal128 d)
- {
- if (Flags.IsFirstForm(d._highBits))
- {
- ulong maxNegativeValue = (ulong)sbyte.MaxValue + 1;
- ulong value;
- if (Decimal128.TryTruncateToUInt64(d, maxNegativeValue, (ulong)sbyte.MaxValue, out value))
- {
- return Decimal128.IsNegative(d) ? (value == maxNegativeValue ? sbyte.MinValue : (sbyte)(-(sbyte)value)) : (sbyte)value;
- }
- else
- {
- throw new OverflowException("Value is too large or too small to be converted to an SByte.");
- }
- }
- else if (Flags.IsSecondForm(d._highBits))
- {
- return 0;
- }
- else
- {
- throw new OverflowException("Infinity or NaN cannot be converted to an SByte.");
- }
- }
- /// <summary>
- /// Converts the value of the specified <see cref="Decimal128"/> to the equivalent <see cref="float"/>.
- /// </summary>
- /// <param name="d">The number to convert.</param>
- /// <returns>A <see cref="float"/> equivalent to <paramref name="d" />.</returns>
- public static float ToSingle(Decimal128 d)
- {
- if (Flags.IsFirstForm(d._highBits))
- {
- // TODO: implement this more efficiently
- var stringValue = d.ToString();
- return float.Parse(stringValue);
- }
- else if (Flags.IsSecondForm(d._highBits))
- {
- return (float)0.0;
- }
- else if (Flags.IsPositiveInfinity(d._highBits))
- {
- return float.PositiveInfinity;
- }
- else if (Flags.IsNegativeInfinity(d._highBits))
- {
- return float.NegativeInfinity;
- }
- else
- {
- return float.NaN;
- }
- }
- /// <summary>
- /// Converts the value of the specified <see cref="Decimal128"/> to the equivalent 16-bit unsigned integer.
- /// </summary>
- /// <param name="d">The number to convert.</param>
- /// <returns>A 16-bit unsigned integer equivalent to <paramref name="d" />.</returns>
- [CLSCompliant(false)]
- public static ushort ToUInt16(Decimal128 d)
- {
- if (Flags.IsFirstForm(d._highBits))
- {
- ulong value;
- if (Decimal128.TryTruncateToUInt64(d, 0, ushort.MaxValue, out value))
- {
- return (ushort)value;
- }
- else
- {
- throw new OverflowException("Value is too large or too small to be converted to a UInt16.");
- }
- }
- else if (Flags.IsSecondForm(d._highBits))
- {
- return 0;
- }
- else
- {
- throw new OverflowException("Infinity or NaN cannot be converted to a UInt16.");
- }
- }
- /// <summary>
- /// Converts the value of the specified <see cref="Decimal128"/> to the equivalent 32-bit unsigned integer.
- /// </summary>
- /// <param name="d">The number to convert.</param>
- /// <returns>A 32-bit unsigned integer equivalent to <paramref name="d" />.</returns>
- [CLSCompliant(false)]
- public static uint ToUInt32(Decimal128 d)
- {
- if (Flags.IsFirstForm(d._highBits))
- {
- ulong value;
- if (Decimal128.TryTruncateToUInt64(d, 0, uint.MaxValue, out value))
- {
- return (uint)value;
- }
- else
- {
- throw new OverflowException("Value is too large or too small to be converted to a UInt32.");
- }
- }
- else if (Flags.IsSecondForm(d._highBits))
- {
- return 0;
- }
- else
- {
- throw new OverflowException("Infinity or NaN cannot be converted to a UInt32.");
- }
- }
- /// <summary>
- /// Converts the value of the specified <see cref="Decimal128"/> to the equivalent 64-bit unsigned integer.
- /// </summary>
- /// <param name="d">The number to convert.</param>
- /// <returns>A 64-bit unsigned integer equivalent to <paramref name="d" />.</returns>
- [CLSCompliant(false)]
- public static ulong ToUInt64(Decimal128 d)
- {
- if (Flags.IsFirstForm(d._highBits))
- {
- ulong value;
- if (Decimal128.TryTruncateToUInt64(d, 0, ulong.MaxValue, out value))
- {
- return value;
- }
- else
- {
- throw new OverflowException("Value is too large or too small to be converted to a UInt64.");
- }
- }
- else if (Flags.IsSecondForm(d._highBits))
- {
- return 0;
- }
- else
- {
- throw new OverflowException("Infinity or NaN cannot be converted to a UInt64.");
- }
- }
- /// <summary>
- /// Converts the string representation of a number to its <see cref="Decimal128" /> equivalent. A return value indicates whether the conversion succeeded or failed.
- /// </summary>
- /// <param name="s">The string representation of the number to convert.</param>
- /// <param name="result">When this method returns, contains the <see cref="Decimal128" /> number that is equivalent to the numeric value contained in <paramref name="s" />, if the conversion succeeded, or is zero if the conversion failed. The conversion fails if the <paramref name="s" /> parameter is null, is not a number in a valid format, or represents a number less than the min value or greater than the max value. This parameter is passed uninitialized.</param>
- /// <returns>
- /// true if <paramref name="s" /> was converted successfully; otherwise, false.
- /// </returns>
- public static bool TryParse(string s, out Decimal128 result)
- {
- if (s == null || s.Length == 0)
- {
- result = default(Decimal128);
- return false;
- }
- const string pattern =
- @"^(?<sign>[+-])?" +
- @"(?<significand>\d+([.]\d*)?|[.]\d+)" +
- @"(?<exponent>[eE](?<exponentSign>[+-])?(?<exponentDigits>\d+))?$";
- var match = Regex.Match(s, pattern);
- if (!match.Success)
- {
- if (s.Equals("Inf", StringComparison.OrdinalIgnoreCase) || s.Equals("Infinity", StringComparison.OrdinalIgnoreCase) ||
- s.Equals("+Inf", StringComparison.OrdinalIgnoreCase) || s.Equals("+Infinity", StringComparison.OrdinalIgnoreCase))
- {
- result = Decimal128.PositiveInfinity;
- return true;
- }
- if (s.Equals("-Inf", StringComparison.OrdinalIgnoreCase) || s.Equals("-Infinity", StringComparison.OrdinalIgnoreCase))
- {
- result = Decimal128.NegativeInfinity;
- return true;
- }
- if (s.Equals("NaN", StringComparison.OrdinalIgnoreCase) || s.Equals("-NaN", StringComparison.OrdinalIgnoreCase))
- {
- result = Decimal128.QNaN;
- return true;
- }
- result = default(Decimal128);
- return false;
- }
- var isNegative = match.Groups["sign"].Value == "-";
- var exponent = 0;
- if (match.Groups["exponent"].Length != 0)
- {
- if (!int.TryParse(match.Groups["exponentDigits"].Value, out exponent))
- {
- result = default(Decimal128);
- return false;
- }
- if (match.Groups["exponentSign"].Value == "-")
- {
- exponent = -exponent;
- }
- }
- var significandString = match.Groups["significand"].Value;
- int decimalPointIndex;
- if ((decimalPointIndex = significandString.IndexOf('.')) != -1)
- {
- exponent -= significandString.Length - (decimalPointIndex + 1);
- significandString = significandString.Substring(0, decimalPointIndex) + significandString.Substring(decimalPointIndex + 1);
- }
- significandString = RemoveLeadingZeroes(significandString);
- significandString = ClampOrRound(ref exponent, significandString);
- if (exponent > __exponentMax || exponent < __exponentMin)
- {
- result = default(Decimal128);
- return false;
- }
- if (significandString.Length > 34)
- {
- result = default(Decimal128);
- return false;
- }
- var significand = UInt128.Parse(significandString);
- result = Decimal128.FromComponents(isNegative, (short)exponent, significand);
- return true;
- }
- // private static methods
- private static string ClampOrRound(ref int exponent, string significandString)
- {
- if (exponent > __exponentMax)
- {
- if (significandString == "0")
- {
- // since significand is zero simply use the largest possible exponent
- exponent = __exponentMax;
- }
- else
- {
- // use clamping to bring the exponent into range
- var numberOfTrailingZeroesToAdd = exponent - __exponentMax;
- var digitsAvailable = 34 - significandString.Length;
- if (numberOfTrailingZeroesToAdd <= digitsAvailable)
- {
- exponent = __exponentMax;
- significandString = significandString + new string('0', numberOfTrailingZeroesToAdd);
- }
- }
- }
- else if (exponent < __exponentMin)
- {
- if (significandString == "0")
- {
- // since significand is zero simply use the smallest possible exponent
- exponent = __exponentMin;
- }
- else
- {
- // use exact rounding to bring the exponent into range
- var numberOfTrailingZeroesToRemove = __exponentMin - exponent;
- if (numberOfTrailingZeroesToRemove < significandString.Length)
- {
- var trailingDigits = significandString.Substring(significandString.Length - numberOfTrailingZeroesToRemove);
- if (Regex.IsMatch(trailingDigits, "^0+$"))
- {
- exponent = __exponentMin;
- significandString = significandString.Substring(0, significandString.Length - numberOfTrailingZeroesToRemove);
- }
- }
- }
- }
- else if (significandString.Length > 34)
- {
- // use exact rounding to reduce significand to 34 digits
- var numberOfTrailingZeroesToRemove = significandString.Length - 34;
- if (exponent + numberOfTrailingZeroesToRemove <= __exponentMax)
- {
- var trailingDigits = significandString.Substring(significandString.Length - numberOfTrailingZeroesToRemove);
- if (Regex.IsMatch(trailingDigits, "^0+$"))
- {
- exponent += numberOfTrailingZeroesToRemove;
- significandString = significandString.Substring(0, significandString.Length - numberOfTrailingZeroesToRemove);
- }
- }
- }
- return significandString;
- }
- private static Decimal128 DecreaseExponent(Decimal128 x, short goal)
- {
- if (Decimal128.IsZero(x))
- {
- // return a zero with the desired exponent
- return Decimal128.FromComponents(Decimal128.IsNegative(x), goal, UInt128.Zero);
- }
- var exponent = GetExponent(x);
- var significand = GetSignificand(x);
- while (exponent > goal)
- {
- var significandTimes10 = UInt128.Multiply(significand, (uint)10);
- if (significandTimes10.CompareTo(Decimal128.__maxSignificand) <= 0)
- {
- break;
- }
- exponent -= 1;
- significand = significandTimes10;
- }
- return Decimal128.FromComponents(Decimal128.IsNegative(x), exponent, significand);
- }
- private static Decimal128 FromComponents(bool isNegative, short exponent, UInt128 significand)
- {
- if (exponent < __exponentMin || exponent > __exponentMax)
- {
- throw new ArgumentOutOfRangeException(nameof(exponent));
- }
- if (significand.CompareTo(__maxSignificand) > 0)
- {
- throw new ArgumentOutOfRangeException(nameof(significand));
- }
- var biasedExponent = MapExponentToDecimal128BiasedExponent(exponent);
- var highBits = ((ulong)biasedExponent << 49) | significand.High;
- if (isNegative)
- {
- highBits = Flags.SignBit | highBits;
- }
- return new Decimal128(highBits, significand.Low);
- }
- private static UInt128 GetSignificand(Decimal128 d)
- {
- return new UInt128(GetSignificandHighBits(d), GetSignificandLowBits(d));
- }
- private static Decimal128 IncreaseExponent(Decimal128 x, short goal)
- {
- if (Decimal128.IsZero(x))
- {
- // return a zero with the desired exponent
- return Decimal128.FromComponents(Decimal128.IsNegative(x), goal, UInt128.Zero);
- }
- var exponent = GetExponent(x);
- var significand = GetSignificand(x);
- while (exponent < goal)
- {
- uint remainder;
- var significandDividedBy10 = UInt128.Divide(significand, (uint)10, out remainder);
- if (remainder != 0)
- {
- break;
- }
- exponent += 1;
- significand = significandDividedBy10;
- }
- return Decimal128.FromComponents(Decimal128.IsNegative(x), exponent, significand);
- }
- private static short MapDecimal128BiasedExponentToExponent(short biasedExponent)
- {
- if (biasedExponent <= 6111)
- {
- return biasedExponent;
- }
- else
- {
- return (short)(biasedExponent - 12288);
- }
- }
- private static ulong MapDecimal128HighBitsToIEEEHighBits(ulong highBits)
- {
- // for Decimal128Bias from 0 to 6111: IEEEBias = Decimal128Bias + 6176
- // for Decimal128Bias from 6112 to 12287: IEEEBias = Decimal128Bias - 6112
- if (Flags.IsFirstForm(highBits))
- {
- var exponentBits = highBits & Flags.FirstFormExponentBits;
- if (exponentBits <= (6111L << 49))
- {
- return highBits + (6176L << 49);
- }
- else
- {
- return highBits - (6112L << 49);
- }
- }
- else if (Flags.IsSecondForm(highBits))
- {
- var exponentBits = highBits & Flags.SecondFormExponentBits;
- if (exponentBits <= (6111L << 47))
- {
- return highBits + (6176L << 47);
- }
- else
- {
- return highBits - (6112L << 47);
- }
- }
- else
- {
- return highBits;
- }
- }
- private static short MapExponentToDecimal128BiasedExponent(short exponent)
- {
- // internally we use a different bias than IEEE so that a Decimal128 struct filled with zero bytes is a true Decimal128 zero
- // Decimal128Bias is defined as:
- // exponents from 0 to 6111: biasedExponent = exponent
- // exponents from -6176 to -1: biasedExponent = exponent + 12288
- if (exponent >= 0)
- {
- return exponent;
- }
- else
- {
- return (short)(exponent + 12288);
- }
- }
- private static ulong MapIEEEHighBitsToDecimal128HighBits(ulong highBits)
- {
- // for IEEEBias from 0 to 6175: Decimal128Bias = IEEEBias + 6112
- // for IEEEBias from 6176 to 12287: Decimal128Bias = IEEEBias - 6176
- if (Flags.IsFirstForm(highBits))
- {
- var exponentBits = highBits & Flags.FirstFormExponentBits;
- if (exponentBits <= (6175L << 49))
- {
- return highBits + (6112L << 49);
- }
- else
- {
- return highBits - (6176L << 49);
- }
- }
- else if (Flags.IsSecondForm(highBits))
- {
- var exponentBits = highBits & Flags.SecondFormExponentBits;
- if (exponentBits <= (6175L << 47))
- {
- return highBits + (6112L << 47);
- }
- else
- {
- return highBits - (6176L << 47);
- }
- }
- else
- {
- return highBits;
- }
- }
- private static string RemoveLeadingZeroes(string significandString)
- {
- if (significandString[0] == '0' && significandString.Length > 1)
- {
- significandString = Regex.Replace(significandString, "^0+", "");
- return significandString.Length == 0 ? "0" : significandString;
- }
- else
- {
- return significandString;
- }
- }
- #endregion
- // private fields
- private readonly ulong _highBits;
- private readonly ulong _lowBits;
- // constructors
- private Decimal128(ulong highBits, ulong lowBits)
- {
- _highBits = highBits;
- _lowBits = lowBits;
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="Decimal128"/> struct.
- /// </summary>
- /// <param name="value">The value.</param>
- public Decimal128(decimal value)
- {
- var bits = decimal.GetBits(value);
- var isNegative = (bits[3] & 0x80000000) != 0;
- var scale = (short)((bits[3] & 0x00FF0000) >> 16);
- var exponent = (short)-scale;
- var significandHigh = (ulong)(uint)bits[2];
- var significandLow = ((ulong)(uint)bits[1] << 32) | (ulong)(uint)bits[0];
- _highBits = (isNegative ? Flags.SignBit : 0) | ((ulong)MapExponentToDecimal128BiasedExponent(exponent) << 49) | significandHigh;
- _lowBits = significandLow;
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="Decimal128"/> struct.
- /// </summary>
- /// <param name="value">The value.</param>
- public Decimal128(double value)
- {
- // TODO: implement this more efficiently
- var stringValue = value.ToString("G17");
- var decimal128Value = Decimal128.Parse(stringValue);
- _highBits = MapIEEEHighBitsToDecimal128HighBits(decimal128Value.GetIEEEHighBits());
- _lowBits = decimal128Value.GetIEEELowBits();
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="Decimal128"/> struct.
- /// </summary>
- /// <param name="value">The value.</param>
- public Decimal128(float value)
- {
- // TODO: implement this more efficiently
- var stringValue = value.ToString("G17");
- var decimal128Value = Decimal128.Parse(stringValue);
- _highBits = MapIEEEHighBitsToDecimal128HighBits(decimal128Value.GetIEEEHighBits());
- _lowBits = decimal128Value.GetIEEELowBits();
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="Decimal128"/> struct.
- /// </summary>
- /// <param name="value">The value.</param>
- public Decimal128(int value)
- {
- if (value >= 0)
- {
- _highBits = 0;
- _lowBits = (ulong)value;
- }
- else
- {
- _highBits = Flags.SignBit;
- _lowBits = value == int.MinValue ? (ulong)int.MaxValue + 1 : (ulong)-value;
- }
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="Decimal128"/> struct.
- /// </summary>
- /// <param name="value">The value.</param>
- public Decimal128(long value)
- {
- if (value >= 0)
- {
- _highBits = 0;
- _lowBits = (ulong)value;
- }
- else
- {
- _highBits = Flags.SignBit;
- _lowBits = value == long.MinValue ? (ulong)long.MaxValue + 1 : (ulong)-value;
- }
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="Decimal128"/> struct.
- /// </summary>
- /// <param name="value">The value.</param>
- [CLSCompliant(false)]
- public Decimal128(uint value)
- {
- _highBits = 0;
- _lowBits = value;
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="Decimal128"/> struct.
- /// </summary>
- /// <param name="value">The value.</param>
- [CLSCompliant(false)]
- public Decimal128(ulong value)
- {
- _highBits = 0;
- _lowBits = value;
- }
- // public methods
- /// <inheritdoc />
- public int CompareTo(Decimal128 other)
- {
- return Decimal128.Compare(this, other);
- }
- /// <inheritdoc />
- public bool Equals(Decimal128 other)
- {
- return Decimal128.Equals(this, other);
- }
- /// <inheritdoc />
- public override bool Equals(object obj)
- {
- if (obj == null || obj.GetType() != typeof(Decimal128))
- {
- return false;
- }
- else
- {
- return Equals((Decimal128)obj);
- }
- }
- /// <inheritdoc />
- public override int GetHashCode()
- {
- int hash = 17;
- hash = 37 * hash + _highBits.GetHashCode();
- hash = 37 * hash + _lowBits.GetHashCode();
- return hash;
- }
- /// <summary>
- /// Gets the high order 64 bits of the binary representation of this instance.
- /// </summary>
- /// <returns>The high order 64 bits of the binary representation of this instance.</returns>
- [CLSCompliant(false)]
- public ulong GetIEEEHighBits()
- {
- return MapDecimal128HighBitsToIEEEHighBits(_highBits);
- }
- /// <summary>
- /// Gets the low order 64 bits of the binary representation of this instance.
- /// </summary>
- /// <returns>The low order 64 bits of the binary representation of this instance.</returns>
- [CLSCompliant(false)]
- public ulong GetIEEELowBits()
- {
- return _lowBits;
- }
- /// <inheritdoc />
- public override string ToString()
- {
- if (Flags.IsFirstForm(_highBits))
- {
- var exponent = GetExponent(this);
- var significand = GetSignificand(this);
- var coefficientString = significand.ToString();
- var adjustedExponent = exponent + coefficientString.Length - 1;
- string result;
- if (exponent > 0 || adjustedExponent < -6)
- {
- result = ToStringWithExponentialNotation(coefficientString, adjustedExponent);
- }
- else
- {
- result = ToStringWithoutExponentialNotation(coefficientString, exponent);
- }
- if (Flags.IsNegative(_highBits))
- {
- result = "-" + result;
- }
- return result;
- }
- else if (Flags.IsSecondForm(_highBits))
- {
- // invalid representation treated as zero
- var exponent = GetExponent(this);
- if (exponent == 0)
- {
- return Flags.IsNegative(_highBits) ? "-0" : "0";
- }
- else
- {
- var exponentString = exponent.ToString(NumberFormatInfo.InvariantInfo);
- if (exponent > 0)
- {
- exponentString = "+" + exponentString;
- }
- return (Flags.IsNegative(_highBits) ? "-0E" : "0E") + exponentString;
- }
- }
- else if (Flags.IsNegativeInfinity(_highBits))
- {
- return "-Infinity";
- }
- else if (Flags.IsPositiveInfinity(_highBits))
- {
- return "Infinity";
- }
- else
- {
- return "NaN";
- }
- }
- // explicit IConvertible implementation
- TypeCode IConvertible.GetTypeCode()
- {
- return TypeCode.Object;
- }
- bool IConvertible.ToBoolean(IFormatProvider provider)
- {
- return !(Decimal128.Equals(this, Decimal128.Zero) || Decimal128.IsNaN(this));
- }
- byte IConvertible.ToByte(IFormatProvider provider)
- {
- return Decimal128.ToByte(this);
- }
- char IConvertible.ToChar(IFormatProvider provider)
- {
- throw new InvalidCastException("Invalid cast from Decima128 to Char.");
- }
- DateTime IConvertible.ToDateTime(IFormatProvider provider)
- {
- throw new InvalidCastException("Invalid cast from Decima128 to DateTime.");
- }
- decimal IConvertible.ToDecimal(IFormatProvider provider)
- {
- return Decimal128.ToDecimal(this);
- }
- double IConvertible.ToDouble(IFormatProvider provider)
- {
- return Decimal128.ToDouble(this);
- }
- short IConvertible.ToInt16(IFormatProvider provider)
- {
- return Decimal128.ToInt16(this);
- }
- int IConvertible.ToInt32(IFormatProvider provider)
- {
- return Decimal128.ToInt32(this);
- }
- long IConvertible.ToInt64(IFormatProvider provider)
- {
- return Decimal128.ToInt64(this);
- }
- sbyte IConvertible.ToSByte(IFormatProvider provider)
- {
- return Decimal128.ToSByte(this);
- }
- float IConvertible.ToSingle(IFormatProvider provider)
- {
- return Decimal128.ToSingle(this);
- }
- string IConvertible.ToString(IFormatProvider provider)
- {
- return ToString();
- }
- object IConvertible.ToType(Type conversionType, IFormatProvider provider)
- {
- var convertible = (IConvertible)this;
- switch (Type.GetTypeCode(conversionType))
- {
- case TypeCode.Boolean: return convertible.ToBoolean(provider);
- case TypeCode.Byte: return convertible.ToByte(provider);
- case TypeCode.Char: return convertible.ToChar(provider);
- case TypeCode.DateTime: return convertible.ToDateTime(provider);
- case TypeCode.Decimal: return convertible.ToDecimal(provider);
- case TypeCode.Double: return convertible.ToDouble(provider);
- case TypeCode.Int16: return convertible.ToInt16(provider);
- case TypeCode.Int32: return convertible.ToInt32(provider);
- case TypeCode.Int64: return convertible.ToInt64(provider);
- case TypeCode.SByte: return convertible.ToSByte(provider);
- case TypeCode.Single: return convertible.ToSingle(provider);
- case TypeCode.String: return convertible.ToString(provider);
- case TypeCode.UInt16: return convertible.ToUInt16(provider);
- case TypeCode.UInt32: return convertible.ToUInt32(provider);
- case TypeCode.UInt64: return convertible.ToUInt64(provider);
- default: throw new InvalidCastException();
- }
- }
- ushort IConvertible.ToUInt16(IFormatProvider provider)
- {
- return Decimal128.ToUInt16(this);
- }
- uint IConvertible.ToUInt32(IFormatProvider provider)
- {
- return Decimal128.ToUInt32(this);
- }
- ulong IConvertible.ToUInt64(IFormatProvider provider)
- {
- return Decimal128.ToUInt64(this);
- }
- // private methods
- private string ToStringWithExponentialNotation(string coefficientString, int adjustedExponent)
- {
- if (coefficientString.Length > 1)
- {
- coefficientString = coefficientString.Substring(0, 1) + "." + coefficientString.Substring(1);
- }
- var exponentString = adjustedExponent.ToString(NumberFormatInfo.InvariantInfo);
- if (adjustedExponent >= 0)
- {
- exponentString = "+" + exponentString;
- }
- return coefficientString + "E" + exponentString;
- }
- private string ToStringWithoutExponentialNotation(string coefficientString, int exponent)
- {
- if (exponent == 0)
- {
- return coefficientString;
- }
- else
- {
- var exponentAbsoluteValue = Math.Abs(exponent);
- var minimumCoefficientStringLength = exponentAbsoluteValue + 1;
- if (coefficientString.Length < minimumCoefficientStringLength)
- {
- coefficientString = coefficientString.PadLeft(minimumCoefficientStringLength, '0');
- }
- var decimalPointIndex = coefficientString.Length - exponentAbsoluteValue;
- return coefficientString.Substring(0, decimalPointIndex) + "." + coefficientString.Substring(decimalPointIndex);
- }
- }
- private static bool TryTruncateToUInt64(Decimal128 d, ulong maxNegativeValue, ulong maxPositiveValue, out ulong value)
- {
- if (Decimal128.IsZero(d))
- {
- value = 0;
- return true;
- }
- var exponent = Decimal128.GetExponent(d);
- var significand = Decimal128.GetSignificand(d);
- if (exponent < 0)
- {
- while (exponent < 0)
- {
- uint remainder; // ignored because we are truncating
- significand = UInt128.Divide(significand, (uint)10, out remainder);
- if (significand.Equals(UInt128.Zero))
- {
- value = 0;
- return true;
- }
- exponent += 1;
- }
- }
- else if (exponent > 0)
- {
- while (exponent > 0)
- {
- significand = UInt128.Multiply(significand, (uint)10);
- if (significand.CompareTo(__maxSignificand) > 0)
- {
- value = 0;
- return false;
- }
- exponent -= 1;
- }
- }
- if (exponent != 0)
- {
- value = 0;
- return false;
- }
- if (significand.High != 0 || significand.Low > (Decimal128.IsNegative(d) ? maxNegativeValue : maxPositiveValue))
- {
- value = 0;
- return false;
- }
- value = significand.Low;
- return true;
- }
- // nested types
- private class Decimal128Comparer : IComparer<Decimal128>
- {
- #region static
- // private static fields
- private static readonly Decimal128Comparer __instance = new Decimal128Comparer();
- // public static properties
- public static Decimal128Comparer Instance
- {
- get { return __instance; }
- }
- #endregion
- // public methods
- public int Compare(Decimal128 x, Decimal128 y)
- {
- var xType = GetDecimal128Type(x);
- var yType = GetDecimal128Type(y);
- var result = xType.CompareTo(yType);
- if (result == 0 && xType == Decimal128Type.Number)
- {
- return CompareNumbers(x, y);
- }
- else
- {
- return result;
- }
- }
- // private methods
- private Decimal128Type GetDecimal128Type(Decimal128 x)
- {
- if (Decimal128.IsNaN(x)) { return Decimal128Type.NaN; }
- else if (Decimal128.IsNegativeInfinity(x)) { return Decimal128Type.NegativeInfinity; }
- else if (Decimal128.IsPositiveInfinity(x)) { return Decimal128Type.PositiveInfity; }
- else { return Decimal128Type.Number; }
- }
- private int CompareNumbers(Decimal128 x, Decimal128 y)
- {
- var xClass = GetNumberClass(x);
- var yClass = GetNumberClass(y);
- var result = xClass.CompareTo(yClass);
- if (result == 0)
- {
- if (xClass == NumberClass.Negative)
- {
- return CompareNegativeNumbers(x, y);
- }
- else if (xClass == NumberClass.Positive)
- {
- return ComparePositiveNumbers(x, y);
- }
- else
- {
- return 0; // else all Zeroes compare equal
- }
- }
- else
- {
- return result;
- }
- }
- private NumberClass GetNumberClass(Decimal128 x)
- {
- if (Decimal128.IsZero(x)) { return NumberClass.Zero; } // must test for Zero first
- else if (Decimal128.IsNegative(x)) { return NumberClass.Negative; }
- else { return NumberClass.Positive; }
- }
- private int CompareNegativeNumbers(Decimal128 x, Decimal128 y)
- {
- return -ComparePositiveNumbers(Decimal128.Negate(x), Decimal128.Negate(y));
- }
- private int ComparePositiveNumbers(Decimal128 x, Decimal128 y)
- {
- var xExponent = GetExponent(x);
- var yExponent = GetExponent(y);
- var exponentDifference = Math.Abs(xExponent - yExponent);
- if (exponentDifference <= 66)
- {
- // we may or may not be able to make the exponents equal but we won't know until we try
- // but we do know we can't eliminate an exponent difference larger than 66
- if (xExponent < yExponent)
- {
- x = IncreaseExponent(x, yExponent);
- y = DecreaseExponent(y, xExponent);
- }
- else if (xExponent > yExponent)
- {
- x = DecreaseExponent(x, yExponent);
- y = IncreaseExponent(y, xExponent);
- }
- }
- if (xExponent == yExponent)
- {
- return GetSignificand(x).CompareTo(GetSignificand(y));
- }
- else
- {
- return xExponent.CompareTo(yExponent);
- }
- }
- private enum Decimal128Type { NaN, NegativeInfinity, Number, PositiveInfity }; // the order matters
- private enum NumberClass { Negative, Zero, Positive }; // the order matters
- }
- private static class Flags
- {
- public const ulong SignBit = 0x8000000000000000;
- public const ulong FirstFormLeadingBits = 0x6000000000000000;
- public const ulong FirstFormLeadingBitsMax = 0x4000000000000000;
- public const ulong FirstFormExponentBits = 0x7FFE000000000000;
- public const ulong FirstFormSignificandBits = 0x0001FFFFFFFFFFFF;
- public const ulong SecondFormLeadingBits = 0x7800000000000000;
- public const ulong SecondFormLeadingBitsMin = 0x6000000000000000;
- public const ulong SecondFormLeadingBitsMax = 0x7000000000000000;
- public const ulong SecondFormExponentBits = 0x1FFF800000000000;
- public const ulong InfinityBits = 0x7C00000000000000;
- public const ulong Infinity = 0x7800000000000000;
- public const ulong SignedInfinityBits = 0xFC00000000000000;
- public const ulong PositiveInfinity = 0x7800000000000000;
- public const ulong NegativeInfinity = 0xF800000000000000;
- public const ulong PartialNaNBits = 0x7C00000000000000;
- public const ulong PartialNaN = 0x7C00000000000000;
- public const ulong NaNBits = 0x7E00000000000000;
- public const ulong QNaN = 0x7C00000000000000;
- public const ulong SNaN = 0x7E00000000000000;
- public static bool IsFirstForm(ulong highBits)
- {
- return (highBits & Flags.FirstFormLeadingBits) <= Flags.FirstFormLeadingBitsMax;
- }
- public static bool IsInfinity(ulong highBits)
- {
- return (highBits & Flags.InfinityBits) == Flags.Infinity;
- }
- public static bool IsNaN(ulong highBits)
- {
- return (highBits & Flags.PartialNaNBits) == Flags.PartialNaN;
- }
- public static bool IsNegative(ulong highBits)
- {
- return (highBits & Flags.SignBit) != 0;
- }
- public static bool IsNegativeInfinity(ulong highBits)
- {
- return (highBits & Flags.SignedInfinityBits) == Flags.NegativeInfinity;
- }
- public static bool IsPositiveInfinity(ulong highBits)
- {
- return (highBits & Flags.SignedInfinityBits) == Flags.PositiveInfinity;
- }
- public static bool IsQNaN(ulong highBits)
- {
- return (highBits & Flags.NaNBits) == Flags.QNaN;
- }
- public static bool IsSecondForm(ulong highBits)
- {
- var secondFormLeadingBits = highBits & Flags.SecondFormLeadingBits;
- return secondFormLeadingBits >= Flags.SecondFormLeadingBitsMin & secondFormLeadingBits <= Flags.SecondFormLeadingBitsMax;
- }
- public static bool IsSNaN(ulong highBits)
- {
- return (highBits & Flags.NaNBits) == Flags.SNaN;
- }
- }
- }
- }
|