Skip to content

Commit

Permalink
Changed minimum/maximum definition for NaN to be in line with IEEE st…
Browse files Browse the repository at this point in the history
…andard.
  • Loading branch information
mgriebling committed Jul 3, 2023
1 parent e531433 commit 7ef8f7d
Show file tree
Hide file tree
Showing 45 changed files with 179 additions and 153 deletions.
45 changes: 29 additions & 16 deletions Sources/BigDecimal/BigDecimal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public struct BigDecimal : Comparable, Equatable, Hashable, Codable {
/// - d: The Double value
public init(_ d: Double) {
if d.isNaN {
self = Self.flagNaN()
self = Self.flagNaN(d.isSignalingNaN)
} else if d.isInfinite {
self = d > 0.0 ? Self.infinity : -Self.infinity
} else {
Expand Down Expand Up @@ -468,8 +468,8 @@ extension BigDecimal {
/// Is *true* if *self* is a finite number
public var isFinite: Bool { !self.isNaN && !self.isInfinite }

/// Is *true* if *self* is a NaN number
public var isNaN: Bool { self.special == .qnan }
/// Is *true* if *self* is either a NaN or SNaN number
public var isNaN: Bool { [Special.qnan, .snan].contains(special) }

/// Is *true* if *self* is a signaling NaN number
public var isSignalingNaN: Bool { self.special == .snan }
Expand Down Expand Up @@ -521,7 +521,7 @@ extension BigDecimal {

/// NaN flag - set to *true* whenever a NaN value is generated
/// Can be set to *false* by application code
public static var NaNFlag = false
public static var nanFlag = false

// MARK: Conversion functions

Expand All @@ -540,7 +540,10 @@ extension BigDecimal {
}

if self.isNaN {
return self.digits.isNegative ? "-NaN" : "NaN"
var flag = "NaN"
if self.isSignalingNaN { flag = "S" + flag }
if self.digits.isNegative { return "-" + flag }
return flag
} else if self.isInfinite {
return self.digits.isNegative ? "-Infinity" : "+Infinity"
}
Expand Down Expand Up @@ -863,7 +866,7 @@ extension BigDecimal {
/// result has infinite decimal expansion
public func pow(_ n: Int, _ rnd: Rounding? = nil) -> Self {
if self.isNaN {
return Self.flagNaN()
return Self.flagNaN(self.isSignalingNaN)
} else if self.isInfinite {
if n < 0 {
return Self.zero
Expand Down Expand Up @@ -893,7 +896,7 @@ extension BigDecimal {
/// - Returns: x \* y
public static func * (x: Self, y: Self) -> Self {
if x.isNaN || y.isNaN {
return Self.flagNaN()
return Self.flagNaN(x.isSignalingNaN || y.isSignalingNaN)
} else if x.isInfinite || y.isInfinite {
if x.isZero || y.isZero {
return Self.flagNaN()
Expand Down Expand Up @@ -937,7 +940,8 @@ extension BigDecimal {

func checkDivision(_ d: Self) -> (failure: Bool, q: Self, r: Self) {
if self.isNaN || d.isNaN {
return (true, Self.flagNaN(), Self.flagNaN())
let signaling = self.isSignalingNaN || d.isSignalingNaN
return (true, Self.flagNaN(signaling), Self.flagNaN(signaling))
} else if self.isInfinite {
if d.isInfinite {
return (true, Self.flagNaN(), Self.flagNaN())
Expand Down Expand Up @@ -1060,19 +1064,27 @@ extension BigDecimal {
/// - Parameters:
/// - x: First operand
/// - y: Second operand
/// - Returns: The larger of *x* and *y*, NaN if either is NaN
/// - Returns: The larger of *x* and *y*, or whichever is a number if the
/// other is NaN.
public static func maximum(_ x: Self, _ y: Self) -> Self {
return (x.isNaN || y.isNaN) ? Self.flagNaN() : (x > y ? x : y)
if x.isSignalingNaN || y.isSignalingNaN { return Self.flagNaN() }
if x.isNaN { return y.isNaN ? Self.flagNaN() : y }
if y.isNaN { return x }
return (x > y ? x : y)
}

/// Minimum
///
/// - Parameters:
/// - x: First operand
/// - y: Second operand
/// - Returns: The smaller of *x* and *y*, NaN if either is NaN
/// - Returns: The minimum of `x` and `y`, or whichever is a number if the
/// other is NaN.
public static func minimum(_ x: Self, _ y: Self) -> Self {
return (x.isNaN || y.isNaN) ? Self.flagNaN() : (x < y ? x : y)
if x.isSignalingNaN || y.isSignalingNaN { return Self.flagNaN() }
if x.isNaN { return y.isNaN ? Self.flagNaN() : y }
if y.isNaN { return x }
return (x < y ? x : y)
}

/// Equal
Expand Down Expand Up @@ -1151,7 +1163,7 @@ extension BigDecimal {
/// - Returns: *self* \* 10^n
public func scale(_ n: Int) -> Self {
if self.isNaN {
return Self.flagNaN()
return Self.flagNaN(self.isSignalingNaN)
} else if self.isInfinite {
return self
} else {
Expand All @@ -1167,7 +1179,7 @@ extension BigDecimal {
/// - Returns: Same value as *self* possibly rounded, with exponent = exp
public func withExponent(_ exp: Int, _ mode: RoundingRule) -> Self {
if self.isNaN || self.isInfinite {
return Self.flagNaN()
return Self.flagNaN(self.isSignalingNaN)
} else if self.exponent > exp {
return Self(self.digits * Rounding.pow10(self.exponent - exp), exp)
} else if self.exponent < exp {
Expand Down Expand Up @@ -1300,8 +1312,9 @@ extension BigDecimal {
return Self(w, e - scale)
}

static func flagNaN() -> Self {
Self.NaNFlag = true
static func flagNaN(_ signaling:Bool=false) -> Self {
if signaling { return Self.signalingNaN }
Self.nanFlag = true
return Self.nan
}
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/BigDecimal/Decimal128.swift
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,8 @@ extension Decimal128 {
let isNegative = self.sign == .minus
if self.isNaN {
return BigDecimal.flagNaN()
} else if self.isSignalingNaN {
return BigDecimal(.snan)
} else if self.isInfinite {
return isNegative ? -BigDecimal.infinity : BigDecimal.infinity
} else {
Expand Down
13 changes: 8 additions & 5 deletions Sources/BigDecimal/Decimal32.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ extension Decimal32 : ExpressibleByIntegerLiteral {

extension Decimal32 : ExpressibleByStringLiteral {
public init(stringLiteral value: StringLiteralType) {
self.init(bid: ID(value).round(Rounding.decimal32))
bid = ID(value).asDecimal32(.bid)
}
}

Expand All @@ -115,7 +115,6 @@ extension Decimal32 : FloatingPoint {

public mutating func round(_ rule: RoundingRule) {
let digits = Rounding.decimal32.precision
bid = Self(self.bd.round(Rounding(rule, digits))).bid
}

///////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -199,12 +198,14 @@ extension Decimal32 : FloatingPoint {
public static func /= (lhs: inout Self, rhs: Self) { lhs = lhs / rhs }

public mutating func formRemainder(dividingBy other: Self) {
bid = Self(bid: self.bd.truncatingRemainder(dividingBy: other.bd)).bid
let q = self / other
let qp = q.rounded(.toNearestOrEven)
let r = self.bd.remainder(dividingBy: other.bd)
print(q,r)
}

public mutating func formTruncatingRemainder(dividingBy other: Self) {
let q = (self/other).rounded(.towardZero)
self -= q * other
bid = Self(bid:self.bd.truncatingRemainder(dividingBy: other.bd)).bid
}

public mutating func formSquareRoot() {
Expand Down Expand Up @@ -357,6 +358,8 @@ extension Decimal32 {
let isNegative = self.sign == .minus
if self.isNaN {
return BigDecimal.flagNaN()
} else if self.isSignalingNaN {
return BigDecimal(.snan)
} else if self.isInfinite {
return isNegative ? -BigDecimal.infinity : BigDecimal.infinity
} else {
Expand Down
2 changes: 2 additions & 0 deletions Sources/BigDecimal/Decimal64.swift
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,8 @@ extension Decimal64 {
let isNegative = self.sign == .minus
if self.isNaN {
return BigDecimal.flagNaN()
} else if self.isSignalingNaN {
return BigDecimal(.snan)
} else if self.isInfinite {
return isNegative ? -BigDecimal.infinity : BigDecimal.infinity
} else {
Expand Down
16 changes: 10 additions & 6 deletions Sources/BigDecimal/DecimalFloatingPoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ extension DecimalType {
s == .minus ? -T.infinity : .infinity
}

/// Biased minimum exponent
// Doesn't change for the different types of Decimals
static var minExponent: Int { -exponentBias }

/// These bit fields can be predetermined just from the size of
Expand Down Expand Up @@ -312,7 +312,8 @@ extension DecimalType {
}
}

public init(nan payload:RawSignificand,signaling:Bool,sign:Sign = .plus) {
public init(nan payload: RawSignificand, signaling: Bool,
sign: Sign = .plus) {
let pattern = signaling ? Self.snanPattern : Self.nanPattern
let man = payload > Self.largestNumber/10 ? 0 : RawBitPattern(payload)
self.init(0)
Expand Down Expand Up @@ -357,7 +358,11 @@ extension DecimalType {
nanBits & Self.nanPattern == Self.infinitePattern<<1
}

public var isNaN: Bool { nanBits & Self.nanPattern == Self.nanPattern }
// Note: Should detect both Nan and SNan
public var isNaN: Bool {
return nanBits & Self.snanPattern == Self.nanPattern
}

public var isSignalingNaN: Bool {
nanBits & Self.snanPattern == Self.snanPattern
}
Expand Down Expand Up @@ -1062,9 +1067,8 @@ extension DecimalFloatingPoint where Self.RawSignificand: FixedWidthInteger {
let r = generator.next(upperBound: max)

// convert the integer to a Decimal number and scale to delta range
var d = Self.init(
sign: delta.sign, exponent: Self.Exponent(delta.exponentBitPattern),
significand: Self.init(r))
var d = Self.init(sign: delta.sign, exponent: delta.exponent,
significand: Self(r))
d += range.lowerBound // add the lower bound
// try again if we failed above
if d == range.upperBound { return random(in: range, using: &generator) }
Expand Down
3 changes: 2 additions & 1 deletion Sources/BigDecimal/Rounding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,10 @@ public struct Rounding: Equatable {
/// - Returns: The value of *x* rounded according to *self*
public func round(_ x: BigDecimal) -> BigDecimal {
if x.isNaN {
if x.isSignalingNaN { return x }
let _ = BigDecimal.flagNaN()
return x
} else if x.isInfinite || x.isSignalingNaN { return x }
} else if x.isInfinite { return x }
let d = x.precision - self.precision
if d <= 0 {
return x
Expand Down
7 changes: 4 additions & 3 deletions Tests/BigDecimalTests/Decimal32Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5357,7 +5357,7 @@ final class Decimal32Tests: XCTestCase {
// Decimal32.rounding = .toNearestOrEven
let s = "123456789"
let y1 = Decimal32(stringLiteral: s)
XCTAssert(y1.description == "1.234568e+8")
XCTAssert(y1.description == "1.234568E+8")
print("\(s) -> \(y1)")

let y = Decimal32(stringLiteral: "234.5")
Expand Down Expand Up @@ -5424,11 +5424,12 @@ final class Decimal32Tests: XCTestCase {
print("Decimal32.signalingNaN =", Decimal32.signalingNaN)
XCTAssert(Decimal32.signalingNaN.description == "SNaN")
print("Decimal32.Infinity =", Decimal32.infinity)
XCTAssert(Decimal32.infinity.description == "Inf")
XCTAssert(Decimal32.infinity.description == "+Infinity")

var a1 = Decimal32(8.625); let b1 = Decimal32(0.75)
let rem = a1.remainder(dividingBy: b1)
print("\(a1).formRemainder(dividingBy: \(b1) = ", rem)
let rem2 = a1.truncatingRemainder(dividingBy: b1)
print("\(a1).formRemainder(dividingBy: \(b1) = ", rem, rem2)
XCTAssert(rem == Decimal32(-0.375))
a1 = Decimal32(8.625)
let q = (a1/b1).rounded(.towardZero); print(q)
Expand Down
4 changes: 2 additions & 2 deletions Tests/BigDecimalTests/TestAbs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import XCTest
class TestAbs: XCTestCase {

override func setUpWithError() throws {
BigDecimal.NaNFlag = false
BigDecimal.nanFlag = false
}

override func tearDownWithError() throws {
XCTAssertFalse(BigDecimal.NaNFlag)
XCTAssertFalse(BigDecimal.nanFlag)
}

struct test {
Expand Down
4 changes: 2 additions & 2 deletions Tests/BigDecimalTests/TestAddition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import XCTest
class TestAddition: XCTestCase {

override func setUpWithError() throws {
BigDecimal.NaNFlag = false
BigDecimal.nanFlag = false
}

override func tearDownWithError() throws {
XCTAssertFalse(BigDecimal.NaNFlag)
XCTAssertFalse(BigDecimal.nanFlag)
}

struct test {
Expand Down
4 changes: 2 additions & 2 deletions Tests/BigDecimalTests/TestArithmetic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import XCTest
class TestArithmetic: XCTestCase {

override func setUpWithError() throws {
BigDecimal.NaNFlag = false
BigDecimal.nanFlag = false
}

override func tearDownWithError() throws {
XCTAssertFalse(BigDecimal.NaNFlag)
XCTAssertFalse(BigDecimal.nanFlag)
}

func doTest1(_ a: BigDecimal, _ b: BigDecimal) {
Expand Down
4 changes: 2 additions & 2 deletions Tests/BigDecimalTests/TestAsString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import XCTest
final class TestAsString: XCTestCase {

override func setUpWithError() throws {
BigDecimal.NaNFlag = false
BigDecimal.nanFlag = false
}

override func tearDownWithError() throws {
XCTAssertFalse(BigDecimal.NaNFlag)
XCTAssertFalse(BigDecimal.nanFlag)
}

struct test {
Expand Down
Loading

0 comments on commit 7ef8f7d

Please sign in to comment.