我有一个公式来计算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。
我可以做些什么来解决这个问题?
答案 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.0
或1/(double)17
。
其次,如果您希望值的总和为1,则可以使用Math.Round(1.0/17*10000)
/10000
来获得点后五位数的精确小数。