M K
M K

Reputation: 31

How to store a very small number

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

Answers (3)

bluesky
bluesky

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

Nicholas Carey
Nicholas Carey

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

usr
usr

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

Related Questions