6

With Xcode 11.1 if I run a playground with:

pow(10 as Double, -2)  // 0.01

I get same output using Float:

pow(10 as Float, -2) // 0.01

But if I try to use the pow(Decimal, Int) as in:

pow(10 as Decimal, -2) // NaN

Does anybody know why?

Is there a better way to deal with positive and negative exponent with pow and Decimal? I need Decimal as they behave as I expect with currency value.

EDIT: I know how to resolve that from math perspective, I'd like to understand why it happens and/or if it can be solved without adding on the cyclomatic complexity of my code (e.g. checking if the exponent is negative and executing 1 / pow)

Andre
  • 1,135
  • 9
  • 20

6 Answers6

6

Well, algebraically, x^(-p) == 1/(x^(p))

So, convert your negative power to a positive power, and then take the reciprocal.

1/pow(10 as Decimal, 2) // 0.01
Jawad Ali
  • 13,556
  • 3
  • 32
  • 49
2

That's simply how NSDecimal / NSDecimalNumber works: it doesn't do negative exponents. You can see a rather elaborate workaround described here:

https://stackoverflow.com/a/12095004/341994

As you can see, the workaround is exactly what you've already been told: look to see if the exponent would be negative and, if so, take the inverse of the positive root.

matt
  • 515,959
  • 87
  • 875
  • 1,141
2

I think that this struct give us an idea about the problem:

public struct Decimal {

    public var _exponent: Int32

    public var _length: UInt32 // length == 0 && isNegative -> NaN

    public var _isNegative: UInt32

    public var _isCompact: UInt32

    public var _reserved: UInt32

    public var _mantissa: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16)

    public init()

    public init(_exponent: Int32, _length: UInt32, _isNegative: UInt32, _isCompact: UInt32, _reserved: UInt32, _mantissa: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16))
}

The length condition should be satisfacted only length == 0, but as UInt32 doesn't represents fractionary numbers the condition is satisfacted...

Carla Camargo
  • 574
  • 5
  • 14
0

... if it can be solved without adding on the cyclomatic complexity of my code ...

extension Decimal {
    func pow(i: Int)->Decimal {
        i < 0 ? 1.0 / Foundation.pow(self, -i) : Foundation.pow(self, i)
    }
}

it is really not so complex to use it, from your example ....

(10 as Decimal).pow(i: -2) // 0.01
user3441734
  • 16,722
  • 2
  • 40
  • 59
0

Kindly find my point of view for this Apple function implementation, Note the following examples:

pow(1 as Decimal, -2) // 1; (1 ^ Any number) = 1
pow(10 as Decimal, -2) // NAN
pow(0.1 as Decimal, -2) // 100
pow(0.01 as Decimal, -2) // 10000
pow(1.5 as Decimal, -2) // NAN
pow(0.5 as Decimal, -2) // NAN

It seems like, pow with decimal don't consider any floating numbers except for 10 basis. So It deals with:

0.1 ^ -2 == (1/10) ^ -2 == 10 ^ 2 // It calculates it appropriately, It's 10 basis 10, 100, 1000, ...

1.5 ^ -2 == (3/2) ^ -2 // (3/2) is a floating number ,so deal with it as Double not decimal, It returns NAN.

0.5 ^ -2 == (1/2) ^ -2 // (2) isn't 10 basis, So It will be dealt as (1/2) as It is, It's a floating number also. It returns NAN.

Amr El-Sayed
  • 384
  • 2
  • 7
-1

I think this could be a bug on Swift's compiler.

EDIT: This is a weird behaviour on Objective-C's NSDecimalNumber, see @matt's comment on this answer below.

As stated by @jawadAli on his answer

Well, algebraically, x^(-p) == 1/(x^(p))

This formula is correct therefore the following statements should be equal

let ten: Decimal = 10
let one: Decimal = 1
let answer: Decimal = 0.01

pow(ten, -2)       // NaN
one / pow(ten, 2)  // 0.01
one / (ten * ten)  // 0.01
answer             // 0.01

Trying this with other data types would result to 0.01.

I also tried to replicate this by using other negative exponents on Decimal data type and it seems to always evaluate to NaN. With the exceptions of 1, 0, and -1

(1...100).forEach {
  print(pow(-2, -$0)) // NaN
  print(pow(-1, -$0)) // Correct
  print(pow(0, -$0))  // Correct
  print(pow(-1, -$0)) // Correct
  print(pow(-2, -$0)) // NaN
}

I would suggest that you use a different Data Type for now.

Zonily Jame
  • 5,053
  • 3
  • 30
  • 56