如何存储一个非常小的数字

时间:2014-09-22 08:16:19

标签: c#

我有一个公式来计算C#中某些数字的权重。我有9个砝码。这些权重之和为1,因此这些权重太小。 例如,在计算这些权重之后,结果如下所示:

1 / 17,1 / 17,1 / 17,1 / 17,1,117,1 / 17,1 / 17,1 / 17,9 / 17

当我想将这些权重存储在double类型的参数中时,存储的值将为:

0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0

问题是我需要在另一个公式中使用这些权重的确切值,并且它们在该公式中太重要了。它们的总和应为1。

我可以做些什么来解决这个问题?

3 个答案:

答案 0 :(得分:12)

A double is perfectly capable of storing about 1/17.无法以完美的准确性和精确度完成这项工作。但也许这对你来说已经足够了。你的错误可能是使用整数除法。试试1.0/17.0

只有可以存储a/b格式的一小部分的数据类型才能准确存储1/17。任何有限精度数都不能(并且由于计算机存储器是有限的,所有数字都是有限精度)。

让我们让SMT Solver Z3提供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

使用以下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对IEEE浮点数进行了比特精确的推理。结果模型是:

(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)))
)

答案 1 :(得分:1)

如果不使用有理数,则难以实现1/17的精确表示。 decimal将为您提供比double更高的精度表示。

答案 2 :(得分:-1)

首先,您不应使用1/17计算确切的值;编译器将其视为int 0,然后将int 0转换为double 0.0。您可以这样编码:1.0/17.01/(double)17

其次,如果您希望值的总和为1,则可以使用Math.Round(1.0/17*10000) /10000来获得点后五位数的精确小数。