Reputation: 31
I have a formula to calculate weights of some numbers in C#. I have 9 weights. The sum of these weights is 1, so these are too small. For instance after calculating these weights, the result is something like below:
1/17, 1/17, 1/17, 1/17, 1/17, 1/17, 1/17, 1/17, 9/17
When I want to store these weights in a parameter with double
type, the stored values will be:
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
The problem is that I need the exact value of these weights to use in another formula, and they are too important in that formula. The sum of them should be 1.
What can I do for solving this problem?
Upvotes: 1
Views: 2405
Reputation: 155
First,you should not calculate the exact value using 1/17
; the compiler consider it as int 0 then converts the int 0 to double 0.0. You can code like this: 1.0/17.0
or 1/(double)17
.
Second, if you want the sum of the values to be 1, you could use Math.Round(1.0/17*10000)
/10000
to get the exact decimal with five numbers after the dot.
Upvotes: -1
Reputation: 74197
Without using rational numbers, an exact representation of 1/17 is going to be difficult to achieve. decimal
will give you a higher precision representation than will double
.
Upvotes: 1
Reputation: 171178
A double
is perfectly capable of storing about 1/17
. It will not be able to do so with perfect accuracy and precision. But maybe that is enough for you. Your mistake probably was to use integer division. Try 1.0/17.0
.
Only a data type that can store a fraction of the form a/b
could exactly store 1/17
. Any finite precision number cannot (and as computer memory is finite, all numbers are finite precision).
Let's ask the SMT Solver Z3 to provide an example where 1.0/divisor*divisor != 0.0
. An example for floats is:
float divisor = (float)(int)(1.296875 * 64);
Console.WriteLine(divisor); //83
Console.WriteLine(1.0f/divisor*divisor == 1.0); //False
Found using the following SMTlib input with Z3 online:
(set-logic QF_FPA)
(declare-const x (_ FP 8 24))
(declare-const divisor (_ FP 8 24))
(declare-const r1 (_ FP 8 24))
(declare-const r2 (_ FP 8 24))
(assert (and
(not (or (= x (as NaN (_ FP 8 24))) (= x (as plusInfinity (_ FP 8 24))) (= x (as minusInfinity (_ FP 8 24)))))
(not (or (= divisor (as NaN (_ FP 8 24))) (= divisor (as plusInfinity (_ FP 8 24))) (= divisor (as minusInfinity (_ FP 8 24)))))
(not (or (= r1 (as NaN (_ FP 8 24))) (= r1 (as plusInfinity (_ FP 8 24))) (= r1 (as minusInfinity (_ FP 8 24)))))
(not (or (= r2 (as NaN (_ FP 8 24))) (= r2 (as plusInfinity (_ FP 8 24))) (= r2 (as minusInfinity (_ FP 8 24)))))
(> divisor ((_ asFloat 8 24) roundTowardZero 2.0 0))
(< divisor ((_ asFloat 8 24) roundTowardZero 100.0 0))
(== divisor (roundToIntegral roundTowardZero divisor))
(= x ((_ asFloat 8 24) roundTowardZero 1.0 0))
(= r1 (/ roundNearestTiesToEven x divisor))
(= r2 (* roundNearestTiesToEven r1 divisor))
(not (== r2 ((_ asFloat 8 24) roundTowardZero 1.0 0)))
))
(check-sat)
(get-model)
Z3 has bit-precise reasoning over IEEE floats. The resulting model is:
(model
(define-fun r2 () (_ FP 8 24)
(as +1.99999988079071044921875p-1 (_ FP 8 24)))
(define-fun divisor () (_ FP 8 24)
(as +1.296875p6 (_ FP 8 24)))
(define-fun x () (_ FP 8 24)
(as +1p0 (_ FP 8 24)))
(define-fun r1 () (_ FP 8 24)
(as +1.54216861724853515625p-7 (_ FP 8 24)))
)
Upvotes: 12