/*
* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
* This source file is part of the Cangjie project, licensed under Apache-2.0
* with Runtime Library Exception.
*
* See https://cangjie-lang.cn/pages/LICENSE for license information.
*/
// The Cangjie API is in Beta. For details on its capabilities and limitations, please refer to the README file.
package std.math.numeric
import std.convert.*
import std.math.*
let UINT64_TENTH_POWER_ARRAY: Array<UInt64> = [
1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
10000000000,
100000000000,
1000000000000,
10000000000000,
100000000000000,
1000000000000000,
10000000000000000,
100000000000000000,
1000000000000000000,
10000000000000000000
]
let BIGINT_TENTH_POWER_ARRAY: Array<BigInt> = [
BigInt(1u64),
BigInt(10u64),
BigInt(100u64),
BigInt(1000u64),
BigInt(10000u64),
BigInt(100000u64),
BigInt(1000000u64),
BigInt(10000000u64),
BigInt(100000000u64),
BigInt(1000000000u64),
BigInt(10000000000u64),
BigInt(100000000000u64),
BigInt(1000000000000u64),
BigInt(10000000000000u64),
BigInt(100000000000000u64),
BigInt(1000000000000000u64),
BigInt(10000000000000000u64),
BigInt(100000000000000000u64),
BigInt(1000000000000000000u64),
BigInt(10000000000000000000u64)
]
let BIGINT_ZERO: BigInt = BigInt(UInt64(0))
let BIGINT_ONE: BigInt = BIGINT_TENTH_POWER_ARRAY[0]
let BIGINT_TEN: BigInt = BIGINT_TENTH_POWER_ARRAY[1]
@OverflowWrapping
func reinterpretAsInt(val: Float32): Int32 {
return Int32(val.toBits())
}
@OverflowWrapping
func reinterpretAsInt(val: Float64): Int64 {
return Int64(val.toBits())
}
/**
* @brief Decimal
*
* @since 0.46.1
*/
public struct Decimal <: Comparable<Decimal> & Hashable & ToString {
var _scale: Int32
var _precision: Int64
var _value: BigInt
var _sign: Int64
/**
* Return the scale of this Decimal.
* A Decimal is presented as (value * (10 ^ {- scale})), and
* this property returns the scale part.
*
* @return: the scale of this Decimal.
*
* @since 0.46.1
*/
public prop scale: Int32 {
get() {
this._scale
}
}
/**
* Return the precision, which is the number of digits in value, of this Decimal.
*
* @return: the scale of this Decimal.
*
* @since 0.46.1
*/
public prop precision: Int64 {
get() {
this._precision
}
}
/**
* Return the digits of this Decimal.
* A Decimal is presented as (value * (10 ^ {- scale}), and
* this property returns the value part.
*
* @return: the unscaled value of this Decimal.
*
* @since 0.46.1
*/
public prop value: BigInt {
get() {
this._value
}
}
/**
* Return the signum function of this Decimal.
*
* @return:
* -1: if this Decimal is negative;
* 0: if this Decimal equals to zero;
* 1: if this Decimal is positive.
*
* @since 0.46.1
*/
public prop sign: Int64 {
get() {
this._sign
}
}
/**
* Translate a string @p val into a Decimal.
* The form of the string @p val follows the BNF 'DecimalString' shown below.
*
* DecimalString
* : (SignString)? ValueString (ExponentString)?
* SignString
* : + | -
* ValueString
* : IntegerPart.(FractionPart)?
* | .FractionPart
* | IntegerPart
* IntegerPart
* : Digits
* FractionPart
* : Digits
* ExponentString
* : ExponentIndicator (SignString)? IntegerPart
* ExponentIndicator
* : e | E
* Digits
* : Digit | Digit Digits
* Digit
* : 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
*
* Translating @p val to a Decimal directly.
*
* @param val: a value translated to a Decimal
*
* @throws IllegalArgumentException if @p val does match the required string format
*
* @since 0.46.1
*/
@Deprecated[message: "Use member function `public static func parse(value: String): Decimal` instead."]
public init(val: String) {
if (val.isEmpty()) {
throw IllegalArgumentException("String value cannot be empty.")
}
var val_scale: Int32 = 0
var val_precision: Int64 = 0
var newValArr: Array<UInt8> = Array<UInt8>(val.size, repeat: 0)
// process sign bit
var offset = 0
var newArrIndex = 0
if (val[offset] == b'-') {
offset++
newValArr[newArrIndex] = b'-'
newArrIndex++
} else if (val[offset] == b'+') {
offset++
newValArr[newArrIndex] = b'+'
newArrIndex++
}
let valLen = val.size
var hasDot = false
var exponent = 0
var hasHeadZero = false
while (offset < valLen) {
// 1. process digits, calculate precision, scale.
let c = val[offset]
if (c >= b'0' && c <= b'9') {
if (c == b'0') {
// check whether head is 0, head 0 is regarded as 0.
if (val_precision == 0) {
hasHeadZero = true
} else {
newValArr[newArrIndex] = c
newArrIndex++
val_precision++
}
} else {
newValArr[newArrIndex] = c
newArrIndex++
val_precision++
}
if (hasDot) {
val_scale++
}
offset++
continue
}
// 2. process r'.'
if (c == b'.') {
if (hasDot) {
throw IllegalArgumentException("String value not allow contains more than one dot.")
}
hasDot = true
offset++
continue
}
// 3. process r'e' | r'E'
if (c == b'e' || c == b'E') {
exponent = parseExponent(val, offset)
break
}
throw IllegalArgumentException(
"String value not allow contains other character beyond [digit, \'.\', e, sign].")
}
// only has head 0, the value is 0.
if (val_precision == 0 && hasHeadZero) {
newValArr[newArrIndex] = b'0'
newArrIndex++
val_precision++
}
// 4. process precision and scale
if (val_precision == 0) {
throw IllegalArgumentException("String value not allow without digits.")
}
if (exponent != 0) {
val_scale = checkAndRtnScale(val_scale, exponent)
}
// 5. process context precison and do round if necessary.
let valString = String.fromUtf8(newValArr[0..newArrIndex])
var val_bigint = BigInt(valString)
// 6. build decimal
this._scale = val_scale
this._precision = val_precision
this._value = val_bigint
this._sign = this._value.sign
}
/**
* Translate a BigInt @p value and an scale into a Decimal.
* The value of the Decimal is (val × (10 ^ {-scale}))
*
* Constructing a Decimal, whose integer part is @p val and scale is @p scale.
*
* @param val: a value translated to a Decimal
* @param scale: the count of numbers after the decimal point
*
*
* @since 0.46.1
*/
public init(val: BigInt, scale: Int32) {
var val_precision = calcNumberOfDigits(val)
this._scale = scale
this._precision = val_precision
this._value = val
this._sign = this._value.sign
}
public init(val: BigInt) {
var val_precision = calcNumberOfDigits(val)
this._scale = 0
this._precision = val_precision
this._value = val
this._sign = this._value.sign
}
/**
* Translate an Int8 type value @p val into a Decimal.
* The decimal sacle value is 0.
* Translating @p val to a Decimal directly.
*
* @param val: a value translated to a Decimal
*
* @since 0.46.1
*/
public init(val: Int8) {
this(Int64(val))
}
/**
* Translate an Int16 type value @p val into a Decimal.
* The decimal sacle value is 0.
* Translating @p val to a Decimal directly.
*
* @param val: a value translated to a Decimal
*
* @since 0.46.1
*/
public init(val: Int16) {
this(Int64(val))
}
/**
* Translate an Int32 type value @p val into a Decimal.
* The decimal sacle value is 0.
* Translating @p val to a Decimal directly.
*
* @param val: a value translated to a Decimal
*
* @since 0.46.1
*/
public init(val: Int32) {
this(Int64(val))
}
/**
* Translate an IntNative type value @p val into a Decimal.
* The decimal sacle value is 0.
* Translating @p val to a Decimal directly.
*
* @param val: a value translated to a Decimal
*
* @since 0.46.1
*/
public init(val: IntNative) {
this(Int64(val))
}
/**
* Translate an Int64 type value @p val into a Decimal.
* The decimal sacle value is 0.
* Translating @p val to a Decimal directly.
*
* @param val: a value translated to a Decimal
*
* @since 0.46.1
*/
public init(val: Int64) {
var val_precision: Int64 = calcNumberOfDigits(val)
var val_scale: Int32 = 0
var val_int64 = val
this._precision = val_precision
this._value = BigInt(val_int64)
this._scale = val_scale
this._sign = this._value.sign
}
/**
* Translate an UInt8 type value @p val into a Decimal.
* The decimal sacle value is 0.
* Translating @p val to a Decimal directly.
*
* @param val: a value translated to a Decimal
*
* @since 0.46.1
*/
public init(val: UInt8) {
this(UInt64(val))
}
/**
* Translate an UInt16 type value @p val into a Decimal.
* The decimal sacle value is 0.
* Translating @p val to a Decimal directly.
*
* @param val: a value translated to a Decimal
*
* @since 0.46.1
*/
public init(val: UInt16) {
this(UInt64(val))
}
/**
* Translate an UInt32 type value @p val into a Decimal.
* The decimal sacle value is 0.
* Translating @p val to a Decimal directly.
*
* @param val: a value translated to a Decimal
*
* @since 0.46.1
*/
public init(val: UInt32) {
this(UInt64(val))
}
/**
* Translate an UIntNative type value @p val into a Decimal.
* The decimal sacle value is 0.
* Translating @p val to a Decimal directly.
*
* @param val: a value translated to a Decimal
*
* @since 0.46.1
*/
public init(val: UIntNative) {
this(UInt64(val))
}
/**
* Translate an UInt64 type value @p val into a Decimal.
* The decimal sacle value is 0.
* Translating @p val to a Decimal directly.
*
* @param val: a value translated to a Decimal
*
* @since 0.46.1
*/
public init(val: UInt64) {
var val_precision: Int64 = calcNumberOfDigits(val)
var val_scale: Int32 = 0
this._precision = val_precision
this._value = BigInt(val)
this._scale = val_scale
this._sign = this._value.sign
}
/**
* Translate a Float16 type value @p val into a Decimal.
* Translating @p val to a Decimal directly.
*
* Note: The results of this constructor can be somewhat unpredictable.
* This constructor provides an exact conversion, and may not have
* the same result as Decimal(v.toString()), where the type of v is Float16.
* For example, use 0.1f16 to constructor a Decimal instance, actually get 0.0999755859375 not equal to 0.1.
*
* Conversely, result of Decimal(string) constructor is perfectly predictable.
* Decimal(string) constructor method is more recommend.
*
* @param val: a value translated to a Decimal
*
* @throws IllegalArgumentException if @p val equals to 'inf', '-inf' or 'nan'
*
* @since 0.46.1
*/
public init(val: Float16) {
this(Float32(val))
}
/**
* Translate a Float32 type value @p val into a Decimal.
* Translating @p val to a Decimal directly.
*
* Note: The results of this constructor can be somewhat unpredictable.
* This constructor provides an exact conversion, and may not have
* the same result as Decimal(v.toString()), where the type of v is Float32.
* For example, use 0.1f32 to constructor a Decimal instance,
* actually get 0.100000001490116119384765625 not equal to 0.1.
*
* Conversely, result of Decimal(string) constructor is perfectly predictable.
* Decimal(string) constructor method is more recommend.
*
* @param val: a value translated to a Decimal
*
* @throws IllegalArgumentException if @p val equals to 'inf', '-inf' or 'nan'
*
* @since 0.46.1
*/
public init(val: Float32) {
if (val.isNaN() || val.isInf()) {
throw IllegalArgumentException("Float value cannot be Inf or NaN.")
}
let f32Bits: Int32 = reinterpretAsInt(val)
// float32 exponent is 8 bits, abs for exponent will never overflow.
var (f_sign, exponent, fraction): (Int32, Int32, Int32) = parseFloat32Bits(f32Bits)
// fraction is 0, means Decimal value is 0
if (fraction == 0) {
this._precision = 1
this._scale = 0
this._value = BigInt(0)
this._sign = 0
return
}
// remove the tailing zero.
while (fraction % 2 == 0) {
fraction = fraction >> 1
exponent++
}
if (Int32(Int64(exponent)) != exponent) {
throw OverflowException("Scale overflow.")
}
var val_bigint: BigInt = BigInt(f_sign * fraction)
// calculate val_bigint
var val_scale: Int32 = 0
if (exponent > 0) {
val_bigint = val_bigint * (BigInt(2) ** (UInt64(exponent)))
} else {
val_bigint = val_bigint * (BigInt(5) ** (UInt64(abs(exponent))))
val_scale = checkScaleOverFlow(Int64(abs(exponent)))
}
var val_precision: Int64 = calcNumberOfDigits(val_bigint)
this._precision = val_precision
this._value = val_bigint
this._scale = val_scale
this._sign = this._value.sign
}
/**
* Translate a Float64 type value @p val into a Decimal.
* Translating @p val to a Decimal directly.
*
* Note: The results of this constructor can be somewhat unpredictable.
* This constructor provides an exact conversion, and may not have
* the same result as Decimal(v.toString()), where the type of v is Float64.
* For example, use 0.1f64 to constructor a Decimal instance,
* actually get 0.1000000000000000055511151231257827021181583404541015625 not equal to 0.1.
*
* Conversely, result of Decimal(string) constructor is perfectly predictable.
* Decimal(string) constructor method is more recommend.
*
* @param val: a value translated to a Decimal
*
* @throws IllegalArgumentException if @p val equals to 'inf', '-inf' or 'nan'
*
* @since 0.46.1
*/
public init(val: Float64) {
if (val.isNaN() || val.isInf()) {
throw IllegalArgumentException("Float value cannot be Inf or NaN.")
}
let f64Bits: Int64 = reinterpretAsInt(val)
// float32 exponent is 11 bits, abs for exponent will never overflow.
var (f_sign, exponent, fraction): (Int64, Int64, Int64) = parseFloat64Bits(f64Bits)
// fraction is 0, means Decimal value is 0
if (fraction == 0) {
this._precision = 1
this._scale = 0
this._value = BigInt(0)
this._sign = this._value.sign
return
}
// remove the tailing zero.
while (fraction % 2 == 0) {
fraction = fraction >> 1
exponent++
}
if (Int64(Int32(exponent)) != exponent) {
throw OverflowException("Scale overflow.")
}
var val_bigint: BigInt = BigInt(f_sign * fraction)
// calculate val_bigint
var val_scale: Int32 = 0
if (exponent > 0) {
val_bigint = val_bigint * (BigInt(2) ** (UInt64(exponent)))
} else {
val_bigint = val_bigint * (BigInt(5) ** (UInt64(abs(exponent))))
val_scale = abs(Int32(exponent))
}
var val_precision: Int64 = calcNumberOfDigits(val_bigint)
this._precision = val_precision
this._value = val_bigint
this._scale = val_scale
this._sign = this._value.sign
}
init(val: BigInt, scale: Int32, precision: Int64) {
this._value = val
this._scale = scale
this._precision = precision
this._sign = this._value.sign
}
/**
* Return a Decimal, whose value is (this + @p d),
* with the precision is 0 which means unlimited precision,
* the specific precision is determined by the calculation result.
* The result has scale = max(this.scale, @p d.scale).
*
* @param d: the value to be added to this Decimal;
*
* @return: the result of (this + @p d).
*
* @throw OverflowException if (this.scale - @p d.scale) exceeds the range of Int32.
*
* @since 0.46.1
*/
func add(d: Decimal): Decimal {
let resValue: BigInt
let valPrecision: Int64
var valScale: Int32 = this._scale
if (this._scale == d.scale) {
resValue = this._value + d.value
valPrecision = calcNumberOfDigits(resValue)
return Decimal(resValue, valScale, valPrecision)
}
let diffScale = Int64(this._scale) - Int64(d.scale)
checkScaleOverFlow(diffScale)
if (diffScale > 0) {
// this._scale bigger than d.scale, modify d.value multiply by 10 ^ diffScale.
resValue = this._value + ailgnBigValue(d.value, diffScale)
} else {
resValue = d.value + ailgnBigValue(this._value, -diffScale)
valScale = d.scale
}
valPrecision = calcNumberOfDigits(resValue)
return Decimal(resValue, valScale, valPrecision)
}
/**
* Overloading r'+' operator.
* Return a Decimal, whose value is (this + @p d),
* with the precision is 0 which means unlimited precision,
* the specific precision is determined by the calculation result.
* The result has scale = max(this.scale, @p d.scale).
*
* @param d: the value to be added to this Decimal;
*
* @return: the result of (this + @p d).
*
* @throw OverflowException if (this.scale - @p d.scale) exceeds the range of Int32.
*
* @since 0.46.1
*/
public operator func +(d: Decimal): Decimal {
return this.add(d)
}
/**
* Return a Decimal, whose value is (this - @p d),
* with the precision is 0 which means infinite precision,
* the specific precision is determined by the calculation result.
* The result has scale = max(this.scale, @p d.scale).
*
* @param d: the value to be substracted from this Decimal;
*
* @return: the result of (this - @p d).
*
* @throw OverflowException if (this.scale - @p d.scale) exceeds the range of Int32.
*
* @since 0.46.1
*/
func sub(d: Decimal): Decimal {
return this.add(d.neg())
}
public operator func -(d: Decimal): Decimal {
return this.add(d.neg())
}
/**
* Return a Decimal, whose value is (this * @p d),
* with the precision is 0 which means infinite precision,
* the specific precision is determined by the calculation result.
* The result has scale = (this.scale + @p d.scale).
*
* @param d: the value to be multiplied by this Decimal;
*
* @return: the result of (this * @p d).
*
* @throws OverflowException if (this.scale + @p d.scale) exceeds the range of Int32.
*
* @since 0.46.1
*/
func mul(d: Decimal): Decimal {
let resScale: Int32 = checkScaleOverFlow(Int64(this._scale) + Int64(d.scale))
if (this._sign == 0 || d.sign == 0) {
return Decimal(BIGINT_ZERO, resScale, 1)
}
let resValue: BigInt = this._value * d.value
let resPrecision: Int64 = calcNumberOfDigits(resValue)
return Decimal(resValue, resScale, resPrecision)
}
public operator func *(d: Decimal): Decimal {
return this.mul(d)
}
func mul(d: Decimal, precision: Int64, roundingMode: RoundingMode) {
var resScale: Int32 = checkScaleOverFlow(Int64(this._scale) + Int64(d.scale))
if (this._sign == 0 || d.sign == 0) {
return Decimal(BIGINT_ZERO, resScale, 1)
}
var resValue: BigInt = this._value * d.value
var resPrecision: Int64 = calcNumberOfDigits(resValue)
if (precision != 0 && resPrecision > precision) {
var roundPrecision: Int64 = resPrecision - precision
while (roundPrecision > 0) {
// process with scale
resScale = checkScaleOverFlow(Int64(resScale) - roundPrecision)
// do rounding return a new bigint object
resValue = doRoundWithCtx(resValue, roundPrecision, roundingMode)
// calculate new bigint number precision
resPrecision = calcNumberOfDigits(resValue)
// if resPrecision still bigger than precison, need do round again.
roundPrecision = resPrecision - precision
}
}
return Decimal(resValue, resScale, resPrecision)
}
/**
* Return a Decimal r, whose value is (this / @p d),
* with the precision is 0 which means infinite precision.
* If the result r is a finite decimal, the specific precision is
* determined by the calculation result.
* If the result r is an infinite decimal, the specific precision
* complies with IEEE 754-2019 decimal128, precision is 34 and
* roundingMode is HalfEven.
*
* @param d: the value that this Decimal is to be divided;
*
* @return: the result of (this / @p d).
*
* @throws IllegalArgumentException if @p d == 0.
* @throws OverflowException if the target Decimal is not in the range of
* [-(maxValue(precision) * (10 ^ {Int32.MAX})), maxValue(precision) * (10 ^ {Int32.MAX})]
*
* @since 0.46.1
*/
func div(d: Decimal): Decimal {
divWithContext(d, RoundingMode.HalfEven)
}
public operator func /(d: Decimal): Decimal {
return this.div(d)
}
/**
* Return a Decimal, whose value is (this / @p d),
* with the precision and roundingMode.
*
* @param d: the value that this Decimal is to be divided;
* @param precision: transfer decimal value to precision.
* @param roundingMode: transfer the value to roundingMode.
*
* @return: the result of (this / @p d) rounded according to the precision and roundingMode.
*
* @throws IllegalArgumentException if @p d == 0.
* @throws OverflowException if the target Decimal is not in the range of
* [-(maxValue(precision) * (10 ^ {Int32.MAX})), maxValue(precision) * (10 ^ {Int32.MAX})]
*
* @since 0.46.1
*/
public func divWithPrecision(d: Decimal, precision: Int64, roundingMode!: RoundingMode = HalfEven): Decimal {
// if precision is 0, process same with without precision paramter.
if (precision == 0) {
return divWithContext(d, roundingMode)
}
if (d.sign == 0) {
throw ArithmeticException("Divided by zero!")
}
let expectedScale: Int64 = Int64(this.scale) - Int64(d.scale)
if (this.sign == 0) {
return Decimal(BIGINT_ZERO, saturateI32(expectedScale), 1)
}
return div(this, d, expectedScale, precision, roundingMode, false)
}
@Deprecated[message: "Use member function `public func divAndMod(d: Decimal): (BigInt, Decimal)` instead."]
public func divAndRem(d: Decimal): (BigInt, Decimal) {
return divAndMod(d)
}
/**
* Return a BigInt i and Remainder r, where i equals to the integer part of (this / d),
* and r satisfies this = (i * d) + r.
*
* @param d: the value that this Decimal is to be divided;
*
* @throws IllegalArgumentException if @p d == 0.
* @throws OverflowException if the target Decimal is not in the range of
* [-(maxValue(precision) * (10 ^ {Int32.MAX})), maxValue(precision) * (10 ^ {Int32.MAX})]
*
* For example, (5.3 / 2.1) = 2 --- 1.1
*
* @since 0.46.1
*/
public func divAndMod(d: Decimal): (BigInt, Decimal) {
var qu = this.divForRem(d)
let re = this.sub(qu.mul(d))
if (qu.scale == 0) {
return (qu.value, re)
} else {
qu = qu.reScale(0, roundingMode: RoundingMode.HalfEven)
return (qu.value, re)
}
}
/**
* Return a Decimal, whose value is (- this),
* with the precision is 0 which means infinite precision,
* the specific precision is determined by the calculation result.
*
* @return: the result of (- this).
*
* @since 0.46.1
*/
func neg(): Decimal {
return Decimal(-(this._value), this._scale, this._precision)
}
public operator func -(): Decimal {
return this.neg()
}
/**
* Return a Decimal, which is equivalent to this one
* with the decimal point moved n places to the left if n >= 0, or
* with the decimal point moved (-n) places to the right if n < 0.
*
* @param n: number of places to move the decimal point.
*
* @return: the result of (this * 10^{-n}), whose digits is the same as this Decimal.
*/
public func shiftPoint(n: Int32): Decimal {
if (n == 0) {
return this
}
let reScale = checkScaleOverFlow(Int64(this.scale) + Int64(n))
if (this.sign == 0 && reScale <= 0) {
return Decimal(BigInt.parse("0", radix: 10))
}
return Decimal(this.value, reScale, this.precision)
}
/**
* Return a Decimal rounded according to @p precision and @p roundingMode.
*
* @param precision: transfer decimal value to precision.
* @param roundingMode: transfer the value to roundingMode.
*
* @return: a Decimal rounded according to @p precision and @p roundingMode.
*
* @throws OverflowException if the target Decimal is not in the range of
* [-(maxValue(precision) * (10 ^ {Int32.MAX})), maxValue(precision) * (10 ^ {Int32.MAX})]
*/
public func roundWithPrecision(precision: Int64, roundingMode!: RoundingMode = RoundingMode.HalfEven): Decimal {
if (precision == 0 || precision >= this.precision) {
return this
}
var unscaledValue: BigInt = this.value
// whether roundPrecision can be overflow
var roundPrecision: Int64 = this.precision - precision
var reScale: Int32 = this.scale
var rePrecision = this.precision
while (roundPrecision > 0) {
reScale = checkScaleOverFlow(Int64(reScale) - roundPrecision)
unscaledValue = doRoundWithCtx(unscaledValue, roundPrecision, roundingMode)
rePrecision = calcNumberOfDigits(unscaledValue)
roundPrecision = rePrecision - precision
}
return Decimal(unscaledValue, reScale, rePrecision)
}
/**
* Return whether this Decimal is less than another Decimal.
*
* @param d: the Decimal for comparison.
*
* @return: true if this Decimal is less than @p d, otherwise false.
*
* @since 0.46.1
*/
public operator func <(d: Decimal): Bool {
return match (this.compare(d)) {
case Ordering.LT => true
case _ => false
}
}
/**
* Return whether this Decimal is greater than another Decimal.
*
* @param d: the Decimal for comparison.
*
* @return: true if this Decimal is greater than @p d, otherwise false.
*
* @since 0.46.1
*/
public operator func >(d: Decimal): Bool {
return match (this.compare(d)) {
case Ordering.GT => true
case _ => false
}
}
/**
* Return whether this Decimal is less than or equal to another Decimal.
*
* @param d: the Decimal for comparison.
*
* @return: true if this Decimal is less than or equal to @p d, otherwise false.
*
* @since 0.46.1
*/
public operator func <=(d: Decimal): Bool {
return match (this.compare(d)) {
case Ordering.GT => false
case _ => true
}
}
/**
* Return whether this Decimal is greater than or equal to another Decimal.
*
* @param d: the Decimal for comparison.
*
* @return: true if this Decimal is greater than or equal to @p d, otherwise false.
*
* @since 0.46.1
*/
public operator func >=(d: Decimal): Bool {
return match (this.compare(d)) {
case Ordering.LT => false
case _ => true
}
}
/**
* Return whether this Decimal is equal to another Decimal.
*
* @param d: the Decimal for comparison.
*
* @return: true if this Decimal is equal to @p d, otherwise false.
*
* @since 0.46.1
*/
public operator func ==(d: Decimal): Bool {
return match (this.compare(d)) {
case Ordering.EQ => true
case _ => false
}
}
/**
* Return whether this Decimal is not equal to another Decimal.
*
* @param d: the Decimal for comparison.
*
* @return: true if this Decimal is not equal to @p d, otherwise false.
*
* @since 0.46.1
*/
public operator func !=(d: Decimal): Bool {
return match (this.compare(d)) {
case Ordering.EQ => false
case _ => true
}
}
/**
* Compare the relationship between this Decimal and another Decimal.
*
* @param d: the Decimal for comparison
*
* @return: the relationship between two Decimal instances.
*
* @since 0.46.1
*/
public func compare(d: Decimal): Ordering {
if (this.sign == 0 && this.sign == d.sign) {
return Ordering.EQ
}
if (this.sign * d.sign <= 0) {
if (this.sign > d.sign) {
return Ordering.GT
} else {
return Ordering.LT
}
}
// this.sign and d.sign is same, not equal 0.
let precisionDiff1: Int64 = this.precision - Int64(this.scale)
let precisionDiff2: Int64 = d.precision - Int64(d.scale)
if (precisionDiff1 > precisionDiff2) {
if (this.sign > 0) {
return Ordering.GT
}
return Ordering.LT
} else if (precisionDiff1 < precisionDiff2) {
if (this.sign > 0) {
return Ordering.LT
}
return Ordering.GT
}
let diffScale = Int64(this._scale) - Int64(d.scale)
checkScaleOverFlow(diffScale)
if (diffScale == 0) {
return this.value.compare(d.value)
}
if (diffScale > 0) {
return this.value.compare(ailgnBigValue(d.value, diffScale))
}
return ailgnBigValue(this.value, -diffScale).compare(d.value)
}
/**
* Return a Decimal, whose value is (this ^ {@p n}),
* with the precision is 0 which means infinite precision,
* the specific precision is determined by the calculation result.
*
* @param n: power to raise this Decimal to.
*
* @return: the result of (this ^ {@p n}).
*
* @throws OverflowException if the target Decimal is not in the range of
* [-(maxValue(precision) * (10 ^ {Int32.MAX})), maxValue(precision) * (10 ^ {Int32.MAX})]
*
* @since 0.46.1
*/
func pow(n: Int64): Decimal {
if (n == 0) {
return Decimal(BIGINT_ONE, 0)
}
if ((Int64(Int32.Min) > n || (Int64(Int32.Max) + 1) < n) && this._scale != 0) {
throw OverflowException("Scale overflow during calculation.")
}
let newScl: Int32 = if (this.scale == 0 || n == Int64.Min) {
0
} else {
checkScaleOverFlow(Int64(this._scale) * abs(n))
}
let bi: BigInt = this._value ** toUInt64(n)
if (n >= 0) {
return Decimal(bi, newScl)
} else {
return Decimal(BIGINT_ONE, 0).div(Decimal(bi, newScl))
}
}
public operator func **(n: Int64): Decimal {
return this.pow(n)
}
/**
* Return a Decimal, whose value is (this ^ {@p n}),
* with the precision and roundingMode.
*
* @param n: power to raise this Decimal to.
* @param precision: transfer decimal value to precision.
* @param roundingMode: transfer the value to roundingMode.
*
* @return: the result of (this ^ {@p n}) rounded according to the precision and roundingMode.
*
* @throws OverflowException if the target Decimal is not in the range of
* [-(maxValue(precision) * (10 ^ {Int32.MAX})), maxValue(precision) * (10 ^ {Int32.MAX})]
*
* @since 0.46.1
*/
public func powWithPrecision(n: Int64, precision: Int64, roundingMode!: RoundingMode = RoundingMode.HalfEven): Decimal {
if (n == 0) {
return Decimal(BIGINT_ONE, 0)
}
if (precision == 0) {
return this.pow(n)
}
var exponent: UInt64 = toUInt64(n)
var newPre: Int64 = if (precision > 0) {
let len = calcNumberOfDigits(exponent)
precision + len + 1
} else {
precision
}
var y: Decimal = Decimal(BIGINT_ONE, 0)
var z: Decimal = this
while (exponent != 0) {
if ((exponent & 1) == 1) {
y = z.mul(y, newPre, roundingMode)
}
z = z.mul(z, newPre, roundingMode)
exponent = exponent >> 1
}
if (n >= 0) {
return y.roundWithPrecision(precision, roundingMode: roundingMode)
} else {
return Decimal(BIGINT_ONE, 0).divWithPrecision(y, precision, roundingMode: roundingMode)
}
}
/**
* Returns a Decimal, whose value is the square root of this Decimal.
* Default precision is 0.
* If the result is a finite decimal, the specific precision is
* determined by the calculation result.
* If the result is an infinite decimal, the specific precision
* complies with IEEE decimal128, precision is 34 and
* roundingMode is HalfEven.
*
* @return: the result of sqrt(this) rounded according to @p precision and @p roundingMode.
*
* @throws IllegalArgumentException if this < 0.
*
* @since 0.46.1
*/
public func sqrtWithPrecision(precision: Int64, roundingMode!: RoundingMode = RoundingMode.HalfEven): Decimal {
if (this._sign == 0) {
return Decimal(BIGINT_ZERO, this._scale >> 1)
}
if (this._sign < 0) {
throw IllegalArgumentException("Negative decimal cannot be squared.")
}
let expectedScale: Int32 = this._scale / 2
var formattedScale: Int32 = checkScaleOverFlow(Int64(this._scale) - this._precision + 1)
if (formattedScale % 2 != 0) {
formattedScale = checkScaleOverFlow(Int64(formattedScale) - 1)
}
let format: Decimal = Decimal(this._value, checkScaleOverFlow(Int64(this._scale) - Int64(formattedScale)),
this._precision)
var ctxPre: Int64 = precision
let expectedPre: Int64 = if (ctxPre == 0) {
this._precision / 2 + 1
} else {
match (roundingMode) {
case HalfUp | HalfEven =>
if (ctxPre <= (Int64.Max >> 1)) {
ctxPre * 2
} else {
Int64.Max - 2
}
case _ => ctxPre
}
}
var x: Decimal = Decimal(sqrt(Float64.parse(format.toString())))
var xPre: Int64 = 10
let fmPre: Int64 = format._precision
do {
let temPre = max(max(xPre, expectedPre + 2), fmPre)
x = Decimal(BigInt.parse("5", radix: 10), 1).mul(x.add(format.divWithPrecision(x, temPre, roundingMode: HalfEven)), temPre,
HalfEven)
xPre *= 2
} while (xPre < expectedPre + 2)
var res: Decimal
if (ctxPre == 0) {
res = if (expectedPre == 0) {
Decimal(x._value, checkScaleOverFlow(Int64(x._scale) - Int64(-formattedScale / 2)), x._precision)
} else {
Decimal(x.value, checkScaleOverFlow(Int64(x._scale) - Int64(-formattedScale / 2)), x._precision)
.roundWithPrecision(expectedPre, roundingMode: roundingMode)
}
if (this.sub(res.pow(2)).compare(Decimal(BIGINT_ZERO, 0)) != EQ) {
return this.sqrtWithPrecision(34, roundingMode: HalfEven)
}
} else {
res = if (expectedPre == 0) {
Decimal(x._value, checkScaleOverFlow(Int64(x._scale) - Int64(-formattedScale / 2)), x._precision)
} else {
Decimal(x._value, checkScaleOverFlow(Int64(x._scale) - Int64(-formattedScale / 2)), x._precision)
.roundWithPrecision(precision, roundingMode: roundingMode)
}
}
if (res.scale != expectedScale) {
res = (res
.removeExtraZero(res.value, res.scale, expectedScale)
.add(Decimal(BIGINT_ZERO, expectedScale)))
.roundWithPrecision(precision, roundingMode: roundingMode)
}
return res
}
/**
* Obtain the hash value of this Decimal.
* Decimal hashcode is based on bigint hashcode and decimal scale value.
*
* @return: hash value of this Decimal.
*
* @since 0.46.1
*/
@OverflowWrapping
public func hashCode(): Int64 {
let rmvTrailingZerosDecimal = this.removeTrailingZeros()
return 31 * rmvTrailingZerosDecimal.value.hashCode() + Int64(rmvTrailingZerosDecimal.scale)
}
/**
* Obtain the string representation of this Decimal.
* The return string is the non-exponential representation of this Decimal.
*
* @return: the string representation of this Decimal.
*
* @since 0.46.1
*/
public func toString(): String {
// scale value is 0, just call bigint value toString().
if (this._scale == 0) {
return this._value.toString()
}
let unscaleValStrArr = this._value.toString().toArray()
// scale value lower than 0, need append tailing 0.
// when scale range is [Int32.Min, 0)
if (this._scale < 0) {
// if sign is 0 means value is 0, no need append tailing 0, return 0.
if (this._sign == 0) {
return "0"
}
let appTailZeroNum = Int64(-this._scale)
var decimalStrArr: Array<Byte> = Array<Byte>(unscaleValStrArr.size + appTailZeroNum, repeat: b'0')
unscaleValStrArr.copyTo(decimalStrArr, 0, 0, unscaleValStrArr.size)
return String.fromUtf8(decimalStrArr)
}
// scale value bigger than 0, need insert decimal point to corresponding position.
// precision is bigger than 0 and scale range [0, Int32.Max]
let decimalPointIndex = this._precision - Int64(this._scale)
let decimalStrArr: Array<Byte>
if (decimalPointIndex == 0) {
decimalStrArr = Array<Byte>(unscaleValStrArr.size + 2, repeat: b'0')
if (unscaleValStrArr[0] == b'-') {
// copy minus sign char to result array
unscaleValStrArr.copyTo(decimalStrArr, 0, 0, 1)
decimalStrArr[2] = b'.'
unscaleValStrArr.copyTo(decimalStrArr, 1, 3, unscaleValStrArr.size - 1)
} else {
decimalStrArr[1] = b'.'
unscaleValStrArr.copyTo(decimalStrArr, 0, 2, unscaleValStrArr.size)
}
} else if (decimalPointIndex > 0) {
// decimalPointIndex bigger than 0, decimal point in the middle of bigint value.
decimalStrArr = Array<Byte>(unscaleValStrArr.size + 1, repeat: b'0')
if (unscaleValStrArr[0] == b'-') {
unscaleValStrArr.copyTo(decimalStrArr, 0, 0, decimalPointIndex + 1)
decimalStrArr[decimalPointIndex + 1] = b'.'
unscaleValStrArr.copyTo(decimalStrArr, decimalPointIndex + 1, decimalPointIndex + 2, Int64(this._scale))
} else {
unscaleValStrArr.copyTo(decimalStrArr, 0, 0, decimalPointIndex)
decimalStrArr[decimalPointIndex] = b'.'
unscaleValStrArr.copyTo(decimalStrArr, decimalPointIndex, decimalPointIndex + 1, Int64(this._scale))
}
} else {
// decimalPointIndex less than 0, decimal point in the left of bigint value.
// when decimalPointIndex less than 0, it's range [-Int32.Max, 0], abs will nerver overflow.
decimalStrArr = Array<Byte>(unscaleValStrArr.size + abs(decimalPointIndex) + 2, repeat: b'0')
if (unscaleValStrArr[0] == b'-') {
decimalStrArr[0] = b'-'
decimalStrArr[2] = b'.'
unscaleValStrArr.copyTo(decimalStrArr, 1, abs(decimalPointIndex) + 3, unscaleValStrArr.size - 1)
} else {
decimalStrArr[1] = b'.'
unscaleValStrArr.copyTo(decimalStrArr, 0, abs(decimalPointIndex) + 2, unscaleValStrArr.size)
}
}
return String.fromUtf8(decimalStrArr)
}
/**
* Return a Decimal, whose scale is specified by @p newScale,
* equal to this Decimal rounded according to roundingMode @p rm.
* Assuming that this Decimal is (n * (10 ^ {- scale})) and we call this.reScale(newScale, rm),
* the return Decimal is (m * (10 ^ {- newScale})) and satisfies:
* (n * (10 ^ {- scale})) = (m * (10 ^ {- newScale})).
* So, m = Int(n * (10 ^ {- scale + newScale})) rounded according to rm.
*
* @param newScale: new scale of the Decimal;
* @param rm: The rounding mode to apply.
*
* @return: a Decimal, whose scale is the specified value, rounded according to @p rm.
*
* @since 0.46.1
*/
public func reScale(newScale: Int32, roundingMode!: RoundingMode = HalfEven): Decimal {
let oldScale = this.scale
if (newScale == oldScale) {
return this
}
if (this.sign == 0) {
return Decimal(BIGINT_ZERO, newScale, 1)
}
if (newScale > oldScale) {
let raise = checkScaleOverFlow(Int64(newScale) - Int64(oldScale))
let bd: BigInt = ailgnBigValue(this.value, Int64(raise))
return Decimal(bd, newScale)
} else {
let drop = checkScaleOverFlow(Int64(oldScale) - Int64(newScale))
return divideAndRound(this.value, tenPowerN(Int64(drop)), newScale, roundingMode, newScale, false)
}
}
/**
* Return a Decimal, which is numerically equal to this one
* but with any trailing zeros removed from the representation.
* For example, if this Decimal is (100 * (10 ^ {- 2})), the result is (1 * (10 ^ {0})).
*
* @return: a numerically equal Decimal with any trailing zeros removed.
*
* @since 0.46.1
*/
public func removeTrailingZeros(): Decimal {
if (this.sign == 0) {
return Decimal(BigInt.parse("0", radix: 10), 0, 1)
}
var unscaledValue = this.value
var scl = scale
var (quotient, remainder): (BigInt, BigInt)
while (unscaledValue.compareMagnitude(BIGINT_TEN) >= 0) {
if (unscaledValue.testBit(0)) {
break
}
(quotient, remainder) = unscaledValue.quoAndRem(BIGINT_TEN)
if (remainder.sign != 0) {
break
}
unscaledValue = quotient
scl = checkScaleOverFlow(Int64(scl) - 1)
}
return Decimal(unscaledValue, scl)
}
/**
* Return a Decimal, whose value part equals to one and scale equals to this.scale.
*
* @return: a Decimal, whose value = 1, and scale = this.scale.
*
* @since 0.46.1
*/
public func scaleUnit(): Decimal {
return Decimal(BIGINT_ONE, this.scale, 1)
}
/**
* Justify whether this Decimal is an integer.
*
* @return: true if this Decimal is an integer, otherwise false.
*
* @since 0.46.1
*/
public func isInteger(): Bool {
if (this.sign == 0 || this.scale <= 0) {
return true
}
var unscaledValue = this.value
var scl = this.scale
while (scl > 0) {
if (this.value.testBit(0)) {
return false
}
let (quotient, remainder): (BigInt, BigInt) = unscaledValue.quoAndRem(BIGINT_TEN)
if (remainder.sign != 0) {
return false
}
unscaledValue = quotient
scl--
}
return true
}
/**
* Convert this Decimal to an Int8.
* Any fractional part of this Decimal will be discarded,
* the integer part of this Decimal will be remained.
* If the integer part of this Decimal is too big to fit in an Int8,
* handling the behavior of integer overflow according to @p overflowHandling.
* The default handling strategy is Throwing OverflowException.
*
* @return: this Decimal to Int8.
*
* @since 0.46.1
*/
public func toInt8(overflowHandling!: OverflowStrategy = Throwing): Int8 {
if (this.sign == 0 || Int64(this.scale) >= this.precision || this.scale <= -8) {
return 0
}
return this.toBigInt().toInt8(overflowHandling: overflowHandling)
}
/**
* Convert this Decimal to an Int16.
* Any fractional part of this Decimal will be discarded,
* the integer part of this Decimal will be remained.
* If the integer part of this Decimal is too big to fit in an Int16,
* handling the behavior of integer overflow according to @p overflowHandling.
* The default handling strategy is Throwing OverflowException.
*
* @return: this Decimal to Int16.
*
* @since 0.46.1
*/
public func toInt16(overflowHandling!: OverflowStrategy = Throwing): Int16 {
if (this.sign == 0 || Int64(this.scale) >= this.precision || this.scale <= -16) {
return 0
}
return this.toBigInt().toInt16(overflowHandling: overflowHandling)
}
/**
* Convert this Decimal to an Int32.
* Any fractional part of this Decimal will be discarded,
* the integer part of this Decimal will be remained.
* If the integer part of this Decimal is too big to fit in an Int32,
* handling the behavior of integer overflow according to @p overflowHandling.
* The default handling strategy is Throwing OverflowException.
*
* @return: this Decimal to Int32.
*
* @since 0.46.1
*/
public func toInt32(overflowHandling!: OverflowStrategy = Throwing): Int32 {
if (this.sign == 0 || Int64(this.scale) >= this.precision || this.scale <= -32) {
return 0
}
return this.toBigInt().toInt32(overflowHandling: overflowHandling)
}
/**
* Convert this Decimal to an Int64.
* Any fractional part of this Decimal will be discarded,
* the integer part of this Decimal will be remained.
* If the integer part of this Decimal is too big to fit in an Int64,
* handling the behavior of integer overflow according to @p overflowHandling.
* The default handling strategy is Throwing OverflowException.
*
* @return: this Decimal to Int64.
*
* @since 0.46.1
*/
public func toInt64(overflowHandling!: OverflowStrategy = Throwing): Int64 {
// return DECIMAL_ZERO in following cases:
// 1. this.sign is 0
// 2. this decimal only has fractional part, this.scale >= this.precision
// 3. a large number, which scale < 0 and -scale > Int.bitLen
if (this.sign == 0 || Int64(this.scale) >= this.precision || this.scale <= -64) {
return 0
}
return this.toBigInt().toInt64(overflowHandling: overflowHandling)
}
/**
* Convert this Decimal to an IntNative.
* Any fractional part of this Decimal will be discarded,
* the integer part of this Decimal will be remained.
* If the integer part of this Decimal is too big to fit in an IntNative,
* handling the behavior of integer overflow according to @p overflowHandling.
* The default handling strategy is Throwing OverflowException.
*
* @return: this Decimal to IntNative.
*
* @since 0.46.1
*/
public func toIntNative(overflowHandling!: OverflowStrategy = Throwing): IntNative {
var nativeLen = 64
if (Int64(IntNative.Max) == Int64(Int32.Max)) {
nativeLen = 32
}
if (this.sign == 0 || Int64(this.scale) >= this.precision || Int64(this.scale) <= -nativeLen) {
return 0
}
return this.toBigInt().toIntNative(overflowHandling: overflowHandling)
}
/**
* Convert this Decimal to an UInt8.
* Any fractional part of this Decimal will be discarded,
* the integer part of this Decimal will be remained.
* If the integer part of this Decimal is too big to fit in an UInt8,
* handling the behavior of integer overflow according to @p overflowHandling.
* The default handling strategy is Throwing OverflowException.
*
* @return: this Decimal to UInt8.
*
* @since 0.46.1
*/
public func toUInt8(overflowHandling!: OverflowStrategy = Throwing): UInt8 {
if (this.sign == 0 || Int64(this.scale) >= this.precision || this.scale <= -8) {
return 0
}
return this.toBigInt().toUInt8(overflowHandling: overflowHandling)
}
/**
* Convert this Decimal to an UInt16.
* Any fractional part of this Decimal will be discarded,
* the integer part of this Decimal will be remained.
* If the integer part of this Decimal is too big to fit in an UInt16,
* handling the behavior of integer overflow according to @p overflowHandling.
* The default handling strategy is Throwing OverflowException.
*
* @return: this Decimal to UInt16.
*
* @since 0.46.1
*/
public func toUInt16(overflowHandling!: OverflowStrategy = Throwing): UInt16 {
if (this.sign == 0 || Int64(this.scale) >= this.precision || this.scale <= -16) {
return 0
}
return this.toBigInt().toUInt16(overflowHandling: overflowHandling)
}
/**
* Convert this Decimal to an UInt32.
* Any fractional part of this Decimal will be discarded,
* the integer part of this Decimal will be remained.
* If the integer part of this Decimal is too big to fit in an UInt32,
* handling the behavior of integer overflow according to @p overflowHandling.
* The default handling strategy is Throwing OverflowException.
*
* @return: this Decimal to UInt32.
*
* @since 0.46.1
*/
public func toUInt32(overflowHandling!: OverflowStrategy = Throwing): UInt32 {
if (this.sign == 0 || Int64(this.scale) >= this.precision || this.scale <= -32) {
return 0
}
return this.toBigInt().toUInt32(overflowHandling: overflowHandling)
}
/**
* Convert this Decimal to an UInt64.
* Any fractional part of this Decimal will be discarded,
* the integer part of this Decimal will be remained.
* If the integer part of this Decimal is too big to fit in an UInt64,
* handling the behavior of integer overflow according to @p overflowHandling.
* The default handling strategy is Throwing OverflowException.
*
* @return: this Decimal to UInt64.
*
* @since 0.46.1
*/
public func toUInt64(overflowHandling!: OverflowStrategy = Throwing): UInt64 {
if (this.sign == 0 || Int64(this.scale) >= this.precision || this.scale <= -64) {
return 0
}
return this.toBigInt().toUInt64(overflowHandling: overflowHandling)
}
/**
* Convert this Decimal to an UIntNative.
* Any fractional part of this Decimal will be discarded,
* the integer part of this Decimal will be remained.
* If the integer part of this Decimal is too big to fit in an UIntNative,
* handling the behavior of integer overflow according to @p overflowHandling.
* The default handling strategy is Throwing OverflowException.
*
* @return: this Decimal to UIntNative.
*
* @since 0.46.1
*/
public func toUIntNative(overflowHandling!: OverflowStrategy = Throwing): UIntNative {
var nativeLen = 64
if (Int64(IntNative.Max) == Int64(Int32.Max)) {
nativeLen = 32
}
if (this.sign == 0 || Int64(this.scale) >= this.precision || Int64(this.scale) <= -nativeLen) {
return 0
}
return this.toBigInt().toUIntNative(overflowHandling: overflowHandling)
}
/**
* Convert this Decimal to an UInt16.
* Any fractional part of this Decimal will be discarded.
*
* @return: this Decimal to BigInt.
*
* @since 0.46.1
*/
public func toBigInt(): BigInt {
// return DECIMAL_ZERO in following cases:
// 1. this.sign is 0
// 2. this decimal only has fractional part, this.scale >= this.precision
if (this.sign == 0 || Int64(this.scale) >= this.precision) {
return BIGINT_ZERO
}
return reScale(0, roundingMode: RoundingMode.Down).value
}
/**
* Convert this Decimal to a Float16.
* If this Decimal is too big to fit in an Float16,
* the result is inf, if this Decimal > 0; or -inf, if this Decimal < 0.
*
* @return: this Decimal to Float16.
*
* @since 0.46.1
*/
public func toFloat16(): Float16 {
if (this.scale <= 0 && this.precision - Int64(this.scale) <= 18) {
if (this.sign > 0) {
return Float16(this.toUInt64())
} else {
return Float16(this.toInt64())
}
}
return Float16.parse(this.toSciString())
}
/**
* Convert this Decimal to a Float32.
* If this Decimal is too big to fit in an Float32,
* the result is inf, if this Decimal > 0; or -inf, if this Decimal < 0.
*
* @return: this Decimal to Float32.
*
* @since 0.46.1
*/
public func toFloat32(): Float32 {
if (this.scale <= 0 && this.precision - Int64(this.scale) <= 18) {
if (this.sign > 0) {
return Float32(this.toUInt64())
} else {
return Float32(this.toInt64())
}
}
return Float32.parse(this.toSciString())
}
/**
* Convert this Decimal to a Float64.
* If this Decimal is too big to fit in an Float64,
* the result is inf, if this Decimal > 0; or -inf, if this Decimal < 0.
*
* @return: this Decimal to Float64.
*
* @since 0.46.1
*/
public func toFloat64(): Float64 {
if (this.scale <= 0 && this.precision - Int64(this.scale) <= 18) {
if (this.sign > 0) {
return Float64(this.toUInt64())
} else {
return Float64(this.toInt64())
}
}
return Float64.parse(this.toSciString())
}
/**
* Convert this Decimal to engineering notation format.
* For example, Decimal.parse("0.00012").toEngString() = 120 * (10 ^ {-6}).
*
* @return: engineering notation for this Decimal.
*
* @since 0.46.1
*/
public func toEngString(): String {
let valArr: Array<Byte> = abs(this.value).toString().toArray()
var resultExponent: Int64 = this.precision - 1 - Int64(this.scale)
var significantDigit = resultExponent % 3
if (significantDigit < 0) {
significantDigit = significantDigit + 3
}
resultExponent = resultExponent - significantDigit
// add default significant digit one.
significantDigit++
let stringBuilder: StringBuilder = StringBuilder()
if (this.sign < 0) {
stringBuilder.append(r'-')
}
if (this.sign == 0) {
match (significantDigit) {
case 1 => stringBuilder.append("0")
case 2 =>
stringBuilder.append("0.00")
resultExponent = resultExponent + 3
case 3 =>
stringBuilder.append("0.0")
resultExponent = resultExponent + 3
case _ => throw IllegalArgumentException("Unexpected significant digit value: ${significantDigit}.")
}
} else if (significantDigit > this.precision) {
stringBuilder.append(String.fromUtf8(valArr))
for (_ in 0..(significantDigit - this.precision)) {
stringBuilder.append(r'0')
}
} else {
stringBuilder.append(String.fromUtf8(valArr[..significantDigit]))
if (this.precision > significantDigit) {
stringBuilder.append(r'.')
stringBuilder.append(String.fromUtf8(valArr[significantDigit..valArr.size]))
}
}
stringBuilder.append(r'E')
stringBuilder.append(resultExponent)
return stringBuilder.toString()
}
/**
* Convert this Decimal scientific notation format.
* For example, Decimal.parse("0.00012").toSciString() = 1.2 * (10 ^ {-4})
*
* @return: scientific notation for this Decimal.
*
* @since 0.46.1
*/
public func toSciString(): String {
let valArr: Array<Byte> = abs(this.value).toString().toArray()
let resultExponent: Int64 = this.precision - 1 - Int64(this.scale)
let stringBuilder: StringBuilder = StringBuilder()
if (this.sign < 0) {
stringBuilder.append(r'-')
}
stringBuilder.append(Rune(UInt32(valArr[0])))
if (this.precision > 1) {
stringBuilder.append(r'.')
stringBuilder.append(String.fromUtf8(valArr[1..valArr.size]))
}
stringBuilder.append(r'E')
stringBuilder.append(resultExponent)
return stringBuilder.toString()
}
private func div(
dividend: Decimal,
divisor: Decimal,
expectedScale: Int64,
cpn: Int64,
roundingMode: RoundingMode,
needDegenerate: Bool
): Decimal {
let eScl: Int32 = checkScaleOverFlow(expectedScale)
let bdividend: BigInt = dividend.value
let bdivisor: BigInt = divisor.value
let dScl: Int64 = dividend.precision
var sScl: Int64 = divisor.precision
// Make the precision of divisor and divisor consistent,
// and then compare them, ensure quotient of x/y within [0.1, 1)
if (compareBySamePrecision(bdividend, dScl, bdivisor, sScl) > 0) {
sScl -= 1
}
let scl = checkScaleOverFlow(expectedScale + sScl - dScl + cpn)
let res: Decimal = if (checkScaleOverFlow(cpn + sScl - dScl) > 0) {
let raise = checkScaleOverFlow(cpn + sScl - dScl)
let bd = ailgnBigValue(bdividend, Int64(raise))
divideAndRound(bd, bdivisor, scl, roundingMode, eScl, needDegenerate)
} else {
let newScale = checkScaleOverFlow(dScl - cpn)
let raise = checkScaleOverFlow(Int64(newScale) - sScl)
let bd = ailgnBigValue(bdivisor, Int64(raise))
divideAndRound(bdividend, bd, scl, roundingMode, eScl, needDegenerate)
}
return res.roundWithPrecision(cpn, roundingMode: roundingMode)
}
private func divideAndRound(
dividend: BigInt,
divisor: BigInt,
scale: Int32,
rdm: RoundingMode,
expectedScale: Int32,
needDegenerate: Bool
): Decimal {
let (qu, re) = dividend.divAndMod(divisor)
if (re.sign != 0) {
if (needDegenerate) {
throw PrecisionMismatchException()
}
let resSign = if (dividend.sign != divisor.sign) {
-1
} else {
1
}
if (needIncrement(divisor, rdm, resSign, qu, re)) {
let (qint, qintArr) = addOneAndOverSize(qu.int, qu.intArr)
let bigSign = resSign <= 0
return Decimal(BigInt(qint, qintArr, bigSign), scale)
}
} else {
if (expectedScale != scale) {
return removeExtraZero(qu, scale, expectedScale)
}
}
return Decimal(qu, scale)
}
func removeExtraZero(v: BigInt, scale: Int32, expectedScale: Int32): Decimal {
var bd = v
var scl = scale
var (qu, re): (BigInt, BigInt)
while (bd.compareMagnitude(BIGINT_TEN) >= 0 && scl > expectedScale) {
if (bd.testBit(0)) {
break
}
(qu, re) = bd.quoAndRem(BIGINT_TEN)
if (re.sign != 0) {
break
}
bd = qu
scl = checkScaleOverFlow(Int64(scl) - 1)
}
return Decimal(bd, scl)
}
private func needIncrement(divisor: BigInt, rdm: RoundingMode, qsign: Int64, qu: BigInt, re: BigInt): Bool {
let cmpDouble = divisor.compareDouble(re)
return needCarry(rdm, qsign, cmpDouble, qu.isOdd())
}
private func checkPrecisionOverFlow(arr: Array<Int64>): Int64 {
try {
var sum: Int64 = Int64(ceil(Float64(arr[0]) / 3.0f64 * 10.0f64))
for (i in arr[1..]) {
sum += i
}
return sum
} catch (e: OverflowException) {
return Int64.Max
}
}
private func compareBySamePrecision(x: BigInt, xscale: Int64, y: BigInt, yscale: Int64): Int64 {
// difference in precision between dividend and divisor
let diff = xscale - yscale
// precision alignment, and then compare two decimal magnitude.
if (diff < 0) {
return ailgnBigValue(x, -diff).compareMagnitude(y)
} else {
return x.compareMagnitude(ailgnBigValue(y, diff))
}
}
@OverflowSaturating
private func saturateI32(scale: Int64): Int32 {
return Int32(scale)
}
private func divForRem(d: Decimal): Decimal {
let expectedScale: Int32 = saturateI32(Int64(this._scale) - Int64(d.scale))
if (this.compareAbsolute(d) < 0) {
return Decimal(BIGINT_ZERO, expectedScale, 1)
}
var tempPre = checkPrecisionOverFlow(d.precision, this.precision, abs(Int64(this.scale) - Int64(d.scale)), 2)
var res: Decimal = this.divWithPrecision(d, tempPre, roundingMode: RoundingMode.Down)
if (res.scale > 0) {
res = res.reScale(0, roundingMode: RoundingMode.Down)
res = removeExtraZero(res.value, 0, expectedScale)
}
if (res.scale < expectedScale) {
res = res.reScale(expectedScale, roundingMode: RoundingMode.HalfEven)
}
return res
}
@OverflowThrowing
private func compareAbsolute(d: Decimal): Int64 {
if (this.sign == 0 && this.sign == d.sign) {
return 0
}
if (this.sign * d.sign == 0) {
if (d.sign == 0) {
return 1
}
return -1
}
// this.sign and d.sign is same, not equal 0.
let precisionDiff1: Int64 = this.precision - Int64(this.scale)
let precisionDiff2: Int64 = d.precision - Int64(d.scale)
if (precisionDiff1 > precisionDiff2) {
return 1
} else if (precisionDiff1 < precisionDiff2) {
return -1
}
let diffScale = Int64(this._scale) - Int64(d.scale)
checkScaleOverFlow(diffScale)
if (diffScale == 0) {
return this.value.compareMagnitude(d.value)
}
if (diffScale > 0) {
return this.value.compareMagnitude(ailgnBigValue(d.value, Int64(diffScale)))
}
return ailgnBigValue(this.value, -diffScale).compareMagnitude(d.value)
}
private func divWithContext(d: Decimal, rdm: RoundingMode): Decimal {
if (d.sign == 0) {
throw ArithmeticException("Divided by zero!")
}
let expectedScale: Int32 = saturateI32(Int64(this.scale) - Int64(d.scale))
if (this.sign == 0) {
return Decimal(BIGINT_ZERO, expectedScale, 1)
} else {
let cpn = checkPrecisionOverFlow(d._precision, this._precision)
var res: Decimal
try {
res = div(this, d, Int64(expectedScale), cpn, rdm, true)
if (expectedScale > res.scale) {
res = reScale(expectedScale, roundingMode: rdm)
}
} catch (e: PrecisionMismatchException) {
res = div(
this,
d,
Int64(expectedScale),
34,
rdm,
false
)
}
return res
}
}
}
func parseExponent(val: String, offset: Int64): Int64 {
var isPositive = true
var expOffset = offset
expOffset++
// check for out of bounds.
if (expOffset >= val.size) {
throw IllegalArgumentException("String value not allow without digits after e.")
}
// process sign bit.
if (val[expOffset] == b'-') {
isPositive = false
expOffset++
} else if (val[expOffset] == b'+') {
expOffset++
}
// if no digit after sign, throw IllegalArgumentException
if (expOffset >= val.size) {
throw IllegalArgumentException("String value not allow without digits after e.")
}
var exponent: Int64 = 0
// remove head 0
while (expOffset < val.size && val[expOffset] == b'0') {
expOffset++
}
// check val size still greater than Int32.Max length, throw OverflowException.
if (val.size - expOffset > 10) {
throw OverflowException("Exponent overflow.")
}
while (expOffset < val.size) {
let c = val[expOffset]
if (c >= b'0' && c <= b'9') {
exponent = exponent * 10 + Int64((c - b'0'))
expOffset++
} else {
// character range [r'0', r'9'], other character is not allowed.
throw IllegalArgumentException(
"String exponent value not allow contains other character beyond [digit, sign].")
}
}
if (!isPositive) {
exponent = -exponent
}
if (Int64(Int32(exponent)) != exponent) {
throw OverflowException("Exponent overflow.")
}
return exponent
}
func checkScaleOverFlow(scale: Int64): Int32 {
try {
return Int32(scale)
} catch (e: OverflowException) {
throw OverflowException("Scale overflow during calculation.")
}
}
@OverflowWrapping
func checkAndRtnScale(scale: Int32, adjustScale: Int64): Int32 {
let newScale = Int64(scale) - adjustScale
if (Int64(Int32(newScale)) != newScale) {
throw OverflowException("Scale overflow.")
}
return Int32(newScale)
}
func doRoundWithCtx(val_bigint: BigInt, roundPrecision: Int64, roundingMode: RoundingMode): BigInt {
let tenPowerDivisior = tenPowerN(roundPrecision)
let (quotient, remainder): (BigInt, BigInt) = val_bigint.divAndMod(tenPowerDivisior)
if (remainder.sign != 0) {
if (needCarry(roundingMode, val_bigint.sign, (remainder << 1).compareMagnitude(tenPowerDivisior),
quotient.testBit(0))) {
if (quotient.sign >= 0) {
return quotient + BIGINT_ONE
}
return quotient - BIGINT_ONE
}
}
return quotient
}
func calcNumberOfDigits(val_bigint: BigInt): Int64 {
if (val_bigint.sign == 0) {
return 1
}
var digitCount = Int64((log10(2.0f64) * Float64(val_bigint.bitLen) + 1.0))
if (digitCount - 1 < BIGINT_TENTH_POWER_ARRAY.size) {
if (BIGINT_TENTH_POWER_ARRAY[digitCount - 1].compareMagnitude(val_bigint) > 0) {
return digitCount - 1
}
return digitCount
}
if ((BIGINT_TEN ** UInt64(digitCount - 1)).compareMagnitude(val_bigint) > 0) {
return digitCount - 1
}
return digitCount
}
func calcNumberOfDigits(value: UInt64): Int64 {
if (value == 0) {
return 1
}
var valueNumCount = 0
while (valueNumCount < UINT64_TENTH_POWER_ARRAY.size && value >= UINT64_TENTH_POWER_ARRAY[valueNumCount]) {
valueNumCount++
}
return valueNumCount
}
func calcNumberOfDigits(value: Int64): Int64 {
return calcNumberOfDigits(toUInt64(value))
}
func toUInt64(i64: Int64): UInt64 {
if (i64 == Int64.Min) {
return 0x8000000000000000u64
}
return UInt64(abs(i64))
}
func tenPowerN(n: Int64): BigInt {
if (n < 0) {
return BIGINT_ZERO
}
let tenPowerValue: BigInt
if (n < BIGINT_TENTH_POWER_ARRAY.size) {
tenPowerValue = BIGINT_TENTH_POWER_ARRAY[n]
} else {
tenPowerValue = BIGINT_TEN ** UInt64(n)
}
return tenPowerValue
}
/**
* Determine whether carry is required based on params.
*
* @param roundingMode: Rounding rules, enum value defined in RoundingMode.
* @param sign: Decimal sign, sign > 0 positive, sign < 0 negative, sign = 0 zero.
* @param halfFlag: Compare with half result, halfFlag > 0 bigger than half, halfFlag < 0 less than half, halfFlag = 0 equals to half.
* @param isOdd: true is an odd number, false is an even number.
*
* @return: whether need carry.
*/
func needCarry(roundingMode: RoundingMode, sign: Int64, halfFlag: Int64, isOdd: Bool): Bool {
match (roundingMode) {
case Up => true
case Down => false
case Ceiling => sign > 0
case Floor => sign < 0
case HalfUp => return halfFlag >= 0
case HalfEven =>
if (halfFlag > 0) {
return true
} else if (halfFlag < 0) {
return false
} else {
return isOdd
}
}
}
func parseFloat32Bits(f32Bits: Int32): (Int32, Int32, Int32) {
let sign: Int32
if ((f32Bits >> 31) == 0) {
sign = 1
} else {
sign = -1
}
let E: Int32 = (f32Bits >> 23) & 0xff
var fraction: Int32
if (E == 0) {
// E equals 0 is denormalized scenario.
// To ensure value smooth conversion from denormalized to normalized,
// exponent = 1 - biased, Float32 biased value is 127. exponent is -126
fraction = (f32Bits & 0x7fffff) << 1
} else {
// E not equals 0 is normalized scenario.
// exponent = E - biased, Float32 biased value is 127. exponent range [-126, 127]
fraction = (f32Bits & 0x7fffff) | 0x800000
}
// exponent = E - 127 - 23
let exponent = E - 150
return (sign, exponent, fraction)
}
func parseFloat64Bits(f64Bits: Int64): (Int64, Int64, Int64) {
let sign: Int64
if ((f64Bits >> 63) == 0) {
sign = 1
} else {
sign = -1
}
let E: Int64 = (f64Bits >> 52) & 0x7ff
var fraction: Int64
if (E == 0) {
// E equals 0 is denormalized scenario.
// To ensure value smooth conversion from denormalized to normalized,
// exponent = 1 - biased, Float64 biased value is 1023. exponent is -1022
fraction = (f64Bits & 0xfffffffffffff) << 1
} else {
// E not equals 0 is normalized scenario.
// exponent = E - biased, Float64 biased value is 1023. exponent range [-1023, 1024]
fraction = (f64Bits & 0xfffffffffffff) | 0x10000000000000
}
// exponent = E - 1023 - 52
let exponent = E - 1075
return (sign, exponent, fraction)
}
func ailgnBigValue(val: BigInt, diffScale: Int64): BigInt {
if (diffScale <= 0) {
return val
}
return val * tenPowerN(diffScale)
}
class PrecisionMismatchException <: Exception {
public init() {
super()
}
public init(message: String) {
super(message)
}
protected override func getClassName(): String {
return "PrecisionMismatchException"
}
}
/**
* Return a Decimal, whose value is the absolute value of this Decimal,
* and whose scale is this.scale.
*
* @param d: the value need to do abs.
*
* @return: a Decimal equals to this.abs().
*
* @since 0.46.1
*/
public func abs(d: Decimal): Decimal {
return Decimal(abs(d.value), d.scale, d.precision)
}
/**
* Return a Decimal rounded to adjacent integer using the specified rounding mode.
*
* @param d: the value need to do round;
* @param roundingMode: transfer the value to roundingMode.
*
* @return: a Decimal rounded according to the rounding mode.
*
* @throws OverflowException if the target Decimal is not in the range of
* [-(maxValue(precision) * (10 ^ {Int32.MAX})), maxValue(precision) * (10 ^ {Int32.MAX})]
*/
public func round(d: Decimal, roundingMode!: RoundingMode = RoundingMode.HalfEven): Decimal {
// The value ranges from -1 to 1.
if (Int64(d.scale) >= d.precision) {
return d.reScale(0, roundingMode: roundingMode)
}
let roundingPre = d.precision - Int64(d.scale)
return d.roundWithPrecision(roundingPre, roundingMode: roundingMode)
}
/**
* Returns a Decimal, whose value is the square root of this Decimal.
* Default precision is 0.
* If the result is a finite decimal, the specific precision is
* determined by the calculation result.
* If the result is an infinite decimal, the specific precision
* complies with IEEE decimal128, precision is 34 and
* roundingMode is HalfEven.
*
* @param d: the value need to do sqrt.
*
* @return: the result of sqrt(this) rounded.
*
* @throws IllegalArgumentException if this < 0.
*
* @since 0.46.1
*/
public func sqrt(d: Decimal): Decimal {
if (d.sign == 0) {
return Decimal(BIGINT_ZERO, d.scale >> 1)
}
if (d.sign < 0) {
throw IllegalArgumentException("Negative decimal cannot be squared.")
}
let expectedScale: Int32 = d.scale / 2
var formattedScale: Int32 = checkScaleOverFlow(Int64(d.scale) - d.precision + 1)
if (formattedScale % 2 != 0) {
formattedScale = checkScaleOverFlow(Int64(formattedScale) - 1)
}
let format: Decimal = Decimal(d.value, checkScaleOverFlow(Int64(d.scale) - Int64(formattedScale)), d.precision)
let expectedPre: Int64 = d.precision / 2 + 1
var x: Decimal = Decimal(sqrt(Float64.parse(format.toString())))
var xPre: Int64 = 10
let fmPre: Int64 = format.precision
do {
let temPre = max(max(xPre, expectedPre + 2), fmPre)
x = Decimal(BigInt.parse("5", radix: 10), 1).mul(x.add(format.divWithPrecision(x, temPre, roundingMode: HalfEven)))
xPre *= 2
} while (xPre < expectedPre + 2)
var res: Decimal
res = if (expectedPre == 0) {
Decimal(x.value, checkScaleOverFlow(Int64(x.scale) - Int64(-formattedScale / 2)), x.precision)
} else {
Decimal(x.value, checkScaleOverFlow(Int64(x.scale) - Int64(-formattedScale / 2)), x.precision)
.roundWithPrecision(expectedPre)
}
if (d.sub(res.pow(2)).compare(Decimal(BIGINT_ZERO, 0)) != EQ) {
return d.sqrtWithPrecision(34, roundingMode: RoundingMode.HalfEven)
}
if (res.scale != expectedScale) {
res = res.removeExtraZero(res.value, res.scale, expectedScale).add(Decimal(BIGINT_ZERO, expectedScale))
}
return res
}
extend Decimal <: Number<Decimal> {}