圆形浮标使它们精确到1

Sch*_*ote 2 python math floating-point python-3.x

我有一个相当粗略的代码,必须或多或少随机生成一堆百分比,存储为十进制浮点数.也就是说,它决定材料一个占总数的13.307%,然后将其存储在dict中为0.13307.

麻烦的是,我永远无法将这些数字加起来只有一个.老实说,我不完全确定问题是什么.这可能与浮子的性质有关.

这是令人讨厌的代码,在其所有过于复杂的荣耀中:

while not sum(atmosphere.values())>=1:
    #Choose a material randomly
    themat=random.choice(list(materials.values()))

    #If the randomly chosen material is gaseous at our predicted temperature...
    if themat.vapor < temp:
        #Choose a random percentage that it will make up of our planet's atmosphere, then put it in the atmos dict.
        atmosphere[themat]=round(random.uniform(0.001,0.5),5)

#Find out if the fractions add up to more than 1
difference=(sum(atmosphere.values())-1)
#If one does...
while difference > 0:
    #Choose a random constituent
    themat=random.choice(list(atmosphere.keys()))
    #If that constituent has a higher fraction value than the amount we'd need to reduce the total to 1...
    if atmosphere[themat]>(sum(atmosphere.values())-1):
        #Subtract that much from it.
        atmosphere[themat]-=difference
        #Then break the loop, since we're done and otherwise we'd go on removing bits of the atmosphere forever.
        break
    else:
        #Otherwise, halve its percentage and reduce difference by the amount we reduced the atmosphere 
        oldperc=atmosphere[themat]
        atmosphere[themat]=oldperc/2
        difference-=oldperc/2

#Then, finally, we correct any overcorrections the previous block made.
difference=(sum(atmosphere.values())-1)
if difference < 0:
    #Choose a random mat
    themat=random.choice(list(atmosphere.keys()))
    #Then add enough to it that the total is 1.
    atmosphere[themat]+=difference
Run Code Online (Sandbox Code Playgroud)

对不起,如果我错过了一些显而易见的事情,或者我没有提供一些重要的信息,但我现在很累,而且我一直在努力解决这个问题.

Pas*_*uoq 5

如果你的意思是找到两个加起来为1.0的值

我知道你想要在0.0和1.0之间选择两个浮点数,使它们加到1.0.

做这个:

  • 挑选两个中最大的L. 它必须介于0.5和1.0之间.
  • 将最小数S定义为1.0 - L.

然后在浮点数,S + L正好是1.0.


如果由于某种原因你在算法中首先获得最小数字S,则计算L = 1.0 - S然后S0 = 1.0 - L.然后L和S0恰好加起来为1.0.考虑S0是S的"圆形"版本.

如果你的意思是几个值X 1,X 2,...,X N.

如果要添加N个数字,每个介于0.0和1.0之间,并且期望操作X 1 + X 2 + ...和1.0 - X 1 ...的行为与数学中的行为相同,这是另一种解决方案.

每次获得新数字X i时,请执行:X i ←1.0 - (1.0 - X i).从那时起仅使用X i的新值.此赋值将略微舍入X i,以使其在所有中间结果介于0.0和1.0之间的总和中表现良好.

编辑:做上述用于值X后1,...,X N-1 ,计算X Ñ如1 - X 1 - ... - X N-1 .这个浮点计算将是精确的(尽管涉及浮点),因此您将完全具有X 1 + ... + X N = 1.

  • @Devolus我认为这可以保证这一点.这是一个定理.你有一些反例用两个数字L和S表示吗? (3认同)

wil*_*ill 5

从您的代码看来,您正在随机生成行星大气,大概是为了某种游戏或其他东西。无论如何,它的随机性让我相信它不需要太准确。

所以我建议你不要使用浮点数,只使用ints 并达到 100。然后你会得到你的确切总和。对于任何你想在 just cast 中使用它们的数学。

这不是一个选择吗?

如果您坚持使用浮点数,请继续阅读...

您使用浮动的问题如下:

浮点数(在本例中为双精度)表示如下:

在此处输入图片说明

对应于 adouble的值:

在此处输入图片说明

所以,

你的号码是(1+M) * 2**(E)(其中E= e-offset

1+M 始终在 1-2 范围内。

因此,我们在每对 2 的幂(正和负)之间有等间距的数字,并且数字之间的间距随着指数 的每增加一倍而加倍E

想想看,这意味着每个数字之间的可表示数字的间距是恒定的[(1,2),(2,4),(4,8), etc]。这也适用于二的负幂,所以:

0.5 - 1
0.25 - 0.5
0.125 - 0.25
0.0625 - 0.125
etc.
Run Code Online (Sandbox Code Playgroud)

并且在每个范围内,都有相同数量的数字。这意味着,如果您将范围内的数字(0.25,0.5)添加到范围内的数字中(0.5,1),那么您有 50% 的机会无法准确表示该数字。

如果对两个指数相差 的浮点数求和D,则总和完全可表示的可能性为 2 -D

如果你想表示范围0-1,那么你必须非常小心你使用的浮点数(即强制N分数的最后一位为零,其中N是 的函数E)。

如果你沿着这条路线走下去,那么你最终会在范围顶部比底部获得更多的浮点数。

另一种方法是决定您希望能够接近零的程度。假设您想降低到 0.0001。

0.0001 = (1+M) * 2 E

日志2 (0.0001) = -13.28771...

所以我们将使用 -14 作为我们的最小指数。

然后要达到 1,我们只需将指数保留为 -1。

所以现在我们有 13 个范围,每个范围的值是下一个的两倍,我们可以求和而不必担心精度。

这也意味着,顶部范围还有 213 个我们可以使用的值。这显然不行。

因此,在选择一个浮点数后,然后将其入到最接近的允许值 - 在这种情况下,舍入我只是将最后 13 位设置为零,然后将其全部放入一个函数中,然后立即将其应用于您的数字你把它们弄出来rand

像这样的东西:

from ctypes import *

def roundf(x,bitsToRound):

    i = cast(pointer(c_float(x)), POINTER(c_int32)).contents.value

    bits = bin(i)

    bits = bits[:-bitsToRound] + "0"*bitsToRound

    i = int(bits,2)

    y = cast(pointer(c_int32(i)), POINTER(c_float)).contents.value

    return y
Run Code Online (Sandbox Code Playgroud)

(图片来自维基百科)