首页 > 解决方案 > 为什么这个函数偶尔会返回错误的值

问题描述

我在 Phrancko.com 上经营一个针织毛衣图案制作网站,我对这个功能正在发生的事情感到困惑。

我使用此函数将用户的“neck_circumference”转换为“neck width”的调整值。

def calc_width_from_neck_size(circ):
  with localcontext() as ctx:
    ctx.prec = 3
    if isinstance(circ, Decimal):
      circ = float(circ)
    diameter = (round((circ / pi) * 8) / 8) + 1.375 # 1 3/8" wider than circular neck
    return Decimal(diameter)

它在大多数情况下始终如一地工作,但偶尔会返回错误的值。当用户在表格中输入颈围时,它只需调用此函数,然后将输入的值和此函数的结果都放入数据库中。中间没有其他处理发生。

例如,这里是为颈部宽度输入 15.5 的用户的所有结果:

id,create_date,neck_width,neck_circumference

80,"2019-08-30 22:23:00",6.250,15.500
81,"2019-08-31 01:54:43",6.250,15.500
111,"2019-11-02 00:33:28",6.250,15.500
238,"2020-03-15 18:36:45",6.250,15.500
258,"2020-05-02 19:02:29",6.250,15.500
289,"2020-07-04 23:56:56",6.750,15.500
321,"2020-09-25 21:47:36",6.250,15.500
323,"2020-09-25 21:56:35",6.250,15.500
330,"2020-10-15 18:47:38",6.250,15.500
350,"2020-10-28 19:33:49",6.250,15.500
352,"2020-11-10 03:16:36",6.250,15.500
360,"2021-01-03 04:31:41",6.250,15.500
385,"2021-02-01 20:25:46",6.250,15.500
397,"2021-02-15 19:28:29",6.250,15.500
426,"2021-03-07 16:52:23",6.250,15.500
469,"2021-05-01 15:21:06",6.250,15.500
478,"2021-05-02 12:53:39",6.250,15.500
502,"2021-05-16 04:51:40",6.250,15.500
515,"2021-05-24 01:10:13",6.250,15.500
534,"2021-06-09 19:59:43",6.250,15.500
547,"2021-06-14 10:24:39",6.250,15.500

请注意,大部分时间都存储了正确的值 6.25,但是对于 id 289,它存储了 6.75!

这不是唯一一次发生这种情况。有数百条这样的记录,只有大约 3 个错误值。它们都发生在不同的日期。如您所见,这是在 2020-07-04 上发布的。2021-05-22 生产了一个不同的版本。代码在这两个日期没有更改和更改回来。此代码在网站的整个生命周期中保持不变。我完全感到困惑。

这个函数怎么能在大多数时候完美地工作,但在极少数情况下会产生错误的值?

附加信息:

我无法提供 MRE,因为我无法生产。这是一些调用该函数一百万次并且不产生单个异常值的测试代码。

from decimal import *
from math import pi


def calc_width_from_neck_size(circ):
  with localcontext() as ctx:
    ctx.prec = 3
    if isinstance(circ, Decimal):
      circ = float(circ)
    diameter = (round((circ / pi) * 8) / 8) + 1.375 # 1 3/8" wider than circular neck
    return Decimal(diameter)

num_found = 0
for count in range(1, 1000000):
    circ = float(Decimal('15.000'))
    test = Decimal(6.125)
    width = calc_width_from_neck_size(circ)
    if width != test:
        num_found += 1
        print(f'WRONG: Count {count} Circ {circ} Width {width} round((circ / pi) * 8) {round((circ / pi) * 8)}')
print(f'Found {num_found} wrong ones.')

因此,我的注意力转向了如何设置输入值 (circ)。它在 Django 视图中运行,下面是调用它的单行代码:

sweater.neck_width = calc_width_from_neck_size(sweater.who_for.neck_circumference)

毛衣.who_for_neck_circumference 字段声明如下:

class Recipient(models.Model):
  knitter = models.ForeignKey(User, on_delete=models.CASCADE)
  recipient_name = models.CharField(max_length=30, verbose_name="Their name:")
  chest_circumference = models.DecimalField(max_digits=5, decimal_places=3)
  neck_circumference = models.DecimalField(max_digits=5, decimal_places=3)
  shoulder_to_waist = models.DecimalField(max_digits=5, decimal_places=3)

该值在 Django 验证后直接从用户输入中提取到此字段中,然后直接传递给如上所示的函数。我不知道当用户输入 DecimalField 时 Django 的幕后会发生什么,但我知道当我提取它时,该值始终是一个有效的 Decimal 值,它直接传递给函数并存储结果在数据库的不同字段中。正如我在原始问题中指出的那样,我只能通过对所有应该相同的字段进行 SQL 显示来查看异常行为。

我明白为什么没有人能帮助我解决这个问题。我只是不知道还能做什么。我很感激任何关于如何阐明这一点的建议。

坦率

标签: pythondjango

解决方案


推荐阅读