Why am I getting wrong return value?

Trying to fmod with a swift function, but my result isn't correct.

Result of d should be 1.7 but I am getting 1.699999999999999.

This is in both the playground and xcode.

It is in the b - (z * c) formula it occurs, any ideas?


func Fmod(b: Double, c: Double) -> Double{

let x:Double = b / c //4.404761904761904

let y:Double = x.truncatingRemainder(dividingBy: 1) //0.404761904761904

let z:Double = x - y // 4

print(" zxc = \(z * c)") // 16.8

print("b-zxc = \(b - z * c)") // 1.699999999999999

print(" b = \(b)") " b = 18.5\n"

return b - (z * c) // 1.699999999999999


}

var d = Fmod(b:18.5, c:4.2) //1.699999999999999

Accepted Reply

The result is correct !


What you see is due to internal representation of Double or Float, which is not a decimal representation, but mantissa (significand) and exponent.


So, it is very hard to predict how it is converted in decimal representation (used for print)


Look here:

https://developer.apple.com/documentation/swift/double/1847553-significand


As an example, consider the results from the following:


let a : Double = 16.8
let b : Double = 18.5
let c = b - a
let d = b + a
let e = a * b
print("a = \(a.significand) * 2**\(a.exponent) = ", a)
print("b = \(b.significand) * 2**\(b.exponent) = ", b)
print("c = \(c.significand) * 2**\(c.exponent) = ", c)
print("d = \(d.significand) * 2**\(d.exponent) = ", d)
print("e = \(e.significand) * 2**\(e.exponent) = ", e)


a = 1.05 * 2**4 = 16.8

b = 1.15625 * 2**4 = 18.5

c = 1.6999999999999993 * 2**0 = 1.6999999999999993

d = 1.103125 * 2**5 = 35.3

e = 1.2140625 * 2**8 = 310.8


The key point is that the significand is kept in the range 1.0 ..< 2.0

So, when you compute b-a, you do not get

(1.15625 - 1.05) * 2**4 which would be 0.10625 * 16 = 1.7


but it is transformed to keep significand between 1 and 2, which causes the approximation.

because 0.10625 is not in the range 1.0 to 2.0, hence we need to convert to a new exponent


But it is pretty hard to predict the result:

if we define

let g = a + c

we get :

g = 1.15625 * 2**4 = 18.5


exactly a !!!! No more rounding error.

c has been converted for the computation from 1.6999999999999993 * 2**0 to 0.10625 * 2**4 = 1.7



If this causes a problem, you should use a decimal representation (but probably not as efficient for heavy computation and anyway, they may use transit conversion to Double ! see h ttps://forums.swift.org/t/weird-behaviour-in-decimal/13893/2).

Hope that's clear.

Replies

The result is correct !


What you see is due to internal representation of Double or Float, which is not a decimal representation, but mantissa (significand) and exponent.


So, it is very hard to predict how it is converted in decimal representation (used for print)


Look here:

https://developer.apple.com/documentation/swift/double/1847553-significand


As an example, consider the results from the following:


let a : Double = 16.8
let b : Double = 18.5
let c = b - a
let d = b + a
let e = a * b
print("a = \(a.significand) * 2**\(a.exponent) = ", a)
print("b = \(b.significand) * 2**\(b.exponent) = ", b)
print("c = \(c.significand) * 2**\(c.exponent) = ", c)
print("d = \(d.significand) * 2**\(d.exponent) = ", d)
print("e = \(e.significand) * 2**\(e.exponent) = ", e)


a = 1.05 * 2**4 = 16.8

b = 1.15625 * 2**4 = 18.5

c = 1.6999999999999993 * 2**0 = 1.6999999999999993

d = 1.103125 * 2**5 = 35.3

e = 1.2140625 * 2**8 = 310.8


The key point is that the significand is kept in the range 1.0 ..< 2.0

So, when you compute b-a, you do not get

(1.15625 - 1.05) * 2**4 which would be 0.10625 * 16 = 1.7


but it is transformed to keep significand between 1 and 2, which causes the approximation.

because 0.10625 is not in the range 1.0 to 2.0, hence we need to convert to a new exponent


But it is pretty hard to predict the result:

if we define

let g = a + c

we get :

g = 1.15625 * 2**4 = 18.5


exactly a !!!! No more rounding error.

c has been converted for the computation from 1.6999999999999993 * 2**0 to 0.10625 * 2**4 = 1.7



If this causes a problem, you should use a decimal representation (but probably not as efficient for heavy computation and anyway, they may use transit conversion to Double ! see h ttps://forums.swift.org/t/weird-behaviour-in-decimal/13893/2).

Hope that's clear.

Thanks, good explanation.