代码中需要修改哪些内容才能达到所需的浮点结果精度?

What needs to be modified in the code to achieve a desired accuracy of floating point result?

提问人:stucknugget 提问时间:1/15/2023 最后编辑:mkrieger1stucknugget 更新时间:1/15/2023 访问量:63

问:

在从麻省理工学院的 OCW 问题集中浏览问题的第三部分时,我遇到了一些疑问。问题描述如下:

C部分:找到合适的储蓄金额:

在 B 部分中,您有机会探讨您每月储蓄的工资百分比和年度加薪如何影响您为首付储蓄所需的时间。这很好,但假设你想设定一个特定的目标,例如能够在三年内支付首付。为了实现这一目标,您每个月应该节省多少钱?在这个问题中,您将编写一个程序来回答该问题。为简化起见,假设:3

  1. 您的半年度加薪为 .07 (7%)
  2. 您的投资年回报率为 0.04 (4%)
  3. 首付为房屋成本的0.25(25%)
  4. 您节省的房屋成本为 100 万美元。

您现在将尝试找到最优惠的储蓄率,以在 36 个月内实现 100 万美元房屋的首付。由于达到这个目标是一个挑战,我们只希望您的储蓄在所需首付的 100 美元以内。在 ps1c.py 中,编写一个程序来计算最佳储蓄率,作为您起薪的函数。您应该使用平分搜索来帮助您有效地执行此操作。您应该跟踪完成平分搜索所需的步骤数。在此问题中,您应该能够重用您为 B 部分编写的一些代码。因为我们正在寻找一个原则上是浮点数的值,所以我们将自己限制在两位小数点的精度(即,我们可能希望以 7.04% 或 0.0704 的小数点保存——但我们不必担心 7.041% 和 7.039% 之间的差异)。这意味着我们可以搜索 0 到 10000 之间的整数(使用整数除法),然后将其转换为小数百分比(使用浮点除法),以便在 36 个月后计算current_savings时使用。通过使用这个范围,我们搜索的数字数量有限,而不是 0 到 1 之间的无限数量的小数。此范围将有助于防止无限循环。我们使用 0 到 10000 的原因是在 0% 到 100% 的范围内考虑两个额外的小数位。您的代码应打印出小数(例如,0.0704 表示 7.04%)。

问题描述后面明确指出,这个问题可以通过实现不同样式的二分搜索以各种不同的方式解决,这最终会得到不同的结果,而且它们都是正确的,即有多种利率可以节省 ~100 的首付。然而,问题的解决方案不再是我关心的问题,因为我意识到我已经解决了它;我现在想知道的是,我必须对代码进行哪些修改,以便我可以生成与下面提供的预期测试输出具有相似精度的输出:

测试用例 1

>>> Enter the starting salary: 150000
Best savings rate: 0.4411
Steps in bisection search: 12

这是我对这个问题的解决方案:

def calc_savings(startingSalary:int, nummonths:int, portion:float):
    """
    Calculated total savings with fixed annual raise and r.o.i for x no. of months 
    at 'portion' percentage of salary saved every month.
    """
    savings = 0
    salary=startingSalary
    for months in range(1, nummonths+1):
        savings+= (salary/12*portion)+(savings*(0.04/12))
        if months%6==0:
            salary = salary+(0.07*salary)

    return savings

cost = 1_000_000
downpayment = cost*0.25
startingsalary = int(input("Enter starting salary: "))
step = 0
high = 10000
low = 0

if startingsalary*3 < downpayment:
    print("Saving the down payment in 36 months with this salary is not possible.")

else:
    while True:
        portion = int((high+low)/2)/10000
        current_savings=calc_savings(startingsalary, 36, portion)

        if downpayment - current_savings < 100 and downpayment-current_savings>=0:
            break
        elif downpayment-current_savings>=100:
            low = portion*10000
            step+=1
        elif downpayment-current_savings < 0:
            high = portion*10000
            step+=1
    print(f"Best savings rate: {portion}")
    print(f"Steps in bisection search: {step}")

这是我得到的结果:

>>> Enter the starting salary: 150000
Best savings rate: 0.441
Steps in bisection search: 12

我意识到这与我选择平分搜索限制的方式以及我后来如何将我从中获得的结果转换回所需的有效位数有关。

在玩了一段时间的代码后,我意识到没有。我的结果中的有效数字与预期结果相同,我通过更改编号来测试这一点。从 36 月到 40 月,并认为它说 0.441,因为它实际上是 0.4410,非常接近 0.4411。

我只是想知道我是否可以对我的代码做些什么来达到确切的 0.4411。

python 浮动精度

评论


答:

1赞 The Photon 1/15/2023 #1

首先,您没有进行浮点优化。即使您对中间步骤使用浮点运算,您也会以定点格式保存优化变量,因此会执行定点优化。当您使用整数和常量比例因子 (100000) 来表示定点而不是浮点的有理数或实数时。

由于您使用的是固定点值,因此如果您想确保获得精确到最接近的 .0001 的结果,您只需更改退出条件即可。就节省的美元而言,您的答案正确到最接近的 100 美元,而不是立即退出,而是等到答案正确到最接近的 0.0001 作为工资的一小部分。由于您的定点表示,这意味着要等到 1 个计数,然后报告哪个数字给出的结果最接近所需的最终节省。highlow

旁注:由于 和 始终是整数,因此您可以使用整数运算来获得与不转换为浮点数并再次转换相同的结果。highlow(high+low)//2int((high+low)/2)

评论

0赞 stucknugget 1/15/2023
首先,感谢您澄清定点与浮点的区别。虽然我明白了那部分,但我无法理解你将退出条件从最接近的 100 更改为最接近的 0.0001 的意思。问题描述说,一旦我们达到首付金额的~100,就退出,所以这就是我所做的。我确定我没有正确解释你说的话。
0赞 The Photon 1/15/2023
你正确地解决了问题,但随后你问如何获得不同的结果。如果你想得到精确到最接近的 0.01% 的答案,你需要做一些不同于解决将答案精确到最接近的 100 美元的问题所需要的事情。
0赞 stucknugget 1/15/2023
现在是 Gotcha。实际上,我之前也在考虑这个问题。你的意思是我将差值从 100 更改为更小的数字以接近 0.4411。谢谢,我前几天看到的这个问题还有另一种解决方案,它涉及实现平分搜索的不同方式,它产生了更好的结果。有什么方法可以向您展示该解决方案,您可以帮助我了解该解决方案中的平分搜索是如何工作的?因为我尝试过但无法完全弄清楚发生了什么。
0赞 The Photon 1/15/2023
不,不要只是改变限制。完全更改测试内容。测试 和 之间的差值是否为 1 或更小。根本不要测试节省的美元。highlow
0赞 stucknugget 1/15/2023
啊。所以你的意思是解决方案是正确的。但要达到这种准确性,我们需要更多的平分搜索步骤?(这样高点和低点之间的差值就会减小到 1 或更小?