I was stunned to learn there is no? immediate way to get the mantissa/exponent of a CGFloat in Swift.
(Note that I want the two values, NOT a string representation in scientific notation format.)
These hopelessly undocumented properties Double.exponent, Double.significand are on Double but that's quite different from wanting the "usual, human" mantissa/exponent. (Unless I'm drastically missing something.)
I typed out a function to do it, but doing so is crap for at least three major reasons that come to mind.
Is there a solution to this conundrum?
(*) Surprised there are not 100 questions about this issue on here!
extension CGFloat { // (ignored negatives for clarity)
var decomp: (CGFloat, CGFloat) {
var exponent: CGFloat = 0.0
var mantissa: CGFloat = self
while mantissa < 1.0 {
mantissa = mantissa * 10.0
exponent -= 1
}
while mantissa >= 10.0 {
mantissa = mantissa / 10.0
exponent += 1
}
print("check ... ", self, "\(mantissa)E\(exponent)")
return (mantissa, exponent)
}
}
hence ..
var x: CGFloat = 0.00123
print( "yo" , x.decomp)
x = 12
print( "yo" , x.decomp)
x = 1000
print( "yo" , x.decomp)
check ... 0.00123 1.23E-3.0 yo (1.23, -3.0) check ... 12.0 1.2E1.0 yo (1.2, 1.0) check ... 1000.0 1.0E3.0 yo (1.0, 3.0)
(†) Minor - I return the exp as a float since that seemed more consistent for the typical ways you'd then use such a thing, but IDK.
The exponent
and significand
properties of Double
gives you the binary significand and exponents. After all, Double
is a binary floating point type.
It seems like you want to get the base-10 exponent and significand. To do that, you should use a Decimal
, which represents a base-10 number.
extension CGFloat {
// this does not consider "special" values like infinities and NaNs
var base10ExponentAndSignificand: (CGFloat, CGFloat) {
let decimal = Decimal(self)
return (
CGFloat(truncating: decimal.significand as NSNumber) * (decimal.isSignMinus ? -1 : 1),
CGFloat(decimal.exponent)
)
}
}
This will produce integers n
, m
where the original number is equal to n * pow(10, m)
.
Scientific notation is usually written as nEm
where n
is a number between 1 and 10. If you want n
to be between 1 and 10, I would just use log10
to calculate the exponent, instead of the looping approach shown in your question:
extension CGFloat {
// this does not consider "special" values like infinities and NaNs
var base10ExponentAndSignificand: (CGFloat, CGFloat) {
guard self != 0 else { return (0, 0) }
let exp = log10(self.magnitude).rounded(.down)
let significand = self / pow(10, exp)
return (significand, exp)
}
}