Testing If a Decimal Is a Whole Number in Swift

Testing if a Decimal is a whole number in Swift

Thanks for the comments! Here is what I am using now.

extension Decimal {
var isWholeNumber: Bool {
return self.isZero || (self.isNormal && self.exponent >= 0)
}
}

Checking if a double value is an integer - Swift

Try 'flooring' the double value then checking if it is unchanged:

let dbl = 2.0
let isInteger = floor(dbl) == dbl // true

Fails if it is not an integer

let dbl = 2.4
let isInteger = floor(dbl) == dbl // false

Check if number is decimal with swift

If you round the number down (which you can do by using the floor function), and then subtract it from the original number, you will get the difference between the two.

if (number - floor(number) > 0.000001) { // 0.000001 can be changed depending on the level of precision you need
// decimal
}

Edit --

My original answer recommended calculating the difference between the number and its floored equivalent to see if there were any units after the decimal points. However, as later described, there may be a rounding error which causes the representation of a value in memory to be slightly different than what it's actually meant to be.

For example, 3.0 could be represented as 3.00000000000001, and therefore the number - floor(number) > 0 would return true, even though it should've theoretically returned false as the offset would be 0.00000000000001.

Therefore please use @jessy's answer below.

Check if NSDecimalNumber is whole number

@interface NSDecimalNumber (IsIntegerNumber)
@property (readonly) BOOL isIntegerNumber;
@end

@implementation NSDecimalNumber (IsIntegerNumber)
-(BOOL)isIntegerNumber {
NSDecimalValue value = [self decimalValue];
if (NSDecimalIsNotANumber(&value)) return NO;
NSDecimal rounded;
NSDecimalRound(&rounded, &value, 0, NSRoundPlain);
return NSDecimalCompare(&rounded, &value) == NSOrderedSame;
}
@end

Display Double as Int if it is a whole number in Swift

Convert Double into Number and then to String if u want to show it on label

let i = 1.0
let j = 1.2345

// Value in String

let a = NSNumber(value: j).stringValue // "1.2345"
let b = NSNumber(value: i).stringValue // "1"

// Value in Double

let c = NSNumber(value: i).doubleValue // 1.0
let d = NSNumber(value: j).doubleValue // 1.2345

Swift Formatting String to have 2 decimal numbers if it is not a whole number

You can extend FloatingPoint to check if it is a whole number and use a condition to set the minimumFractionDigits property of the NumberFormatter to 0 in case it is true otherwise set it to 2:

extension Formatter {
static let custom: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 2
return formatter
}()
}
extension FloatingPoint {
var isWholeNumber: Bool { isNormal ? self == rounded() : isZero }
var custom: String {
Formatter.custom.minimumFractionDigits = isWholeNumber ? 0 : 2
return Formatter.custom.string(for: self) ?? ""
}
}

Playground testing:

1.0.custom    // "1"
1.5.custom // "1.50"
1.75.custom // "1.75"

Getting the decimal part of a double in Swift

Without converting it to a string, you can round up to a number of decimal places like this:

let x:Double = 1234.5678
let numberOfPlaces:Double = 4.0
let powerOfTen:Double = pow(10.0, numberOfPlaces)
let targetedDecimalPlaces:Double = round((x % 1.0) * powerOfTen) / powerOfTen

Your output would be

0.5678

Count number of decimal places in a Float (or Decimal) in Swift

Doing this with Decimal is fairly straightforward, provided you correctly create your Decimal. Decimals are stored as significand * 10^exponent. significand is normalized to the smallest integer possible. So for 1230, the significand is 123 and the exponent is 1. For 1.23 the significand is also 123 and the exponent is -2. That leads us to:

extension Decimal {
var significantFractionalDecimalDigits: Int {
return max(-exponent, 0)
}
}

However, you must be very careful constructing your Decimal. If you construct it from a Double, you will already have applied binary rounding errors. So for example:

let n = Decimal(0.111) // 0.11100000000000002048 because you passed a Double
n.significantFractionalDecimalDigits // 20

vs.

let n = Decimal(string: "0.111")!
n.significantFractionalDecimalDigits // 3 what you meant

Keep in mind of course that Decimal has a maximum number of significant digits, so it may still apply rounding.

let n = Decimal(string: "12345678901235678901234567890.1234567890123456789")!
n.significantFractionalDecimalDigits // 9 ("should" be 19)

And if you're walking down this road at all, you really must read the Floating Point Guide and the canonical StackOverflow question: Is floating point math broken?



Related Topics



Leave a reply



Submit