Crowdmatching dollar amounts inaccurate

Ironically, this highlights a subtle problem of rounding as it applies to discrete numbers. Since we’re dealing with tenths, fully 1/10 of them are exactly halfway between the whole number (0.5, 1.5, 2.5, etc). This means that 10% of the time, we’d be inflating the donation value. (This isn’t a problem with real numbers, since e.g. 1.5000000000000000000000 is pretty rare.)

To deal with this, there’s an alternate rounding scheme that “rounds to even”: 1.5 and 2.5 both round to 2, 3.5 and 4.5 round to 4, etc. This gets rid of the systematic bias, but is clearly more complicated. In fact, rounding is complicated enough that the Wikipedia article lists over a dozen methods!

My experience tells me we should not introduce rounding at all in the backend calculations. We have no need for it, and it just invites trouble. It makes audits harder and mistakes easier.

Whether or not we round any numbers on the frontend can still be discussed…

2 Appreciations

In real terms, how much money is a project losing if we always round down?

At 100 patrons, it’s 10% max (technically 9%, but I like round numbers). By 1000 patrons, it’s 1%. 10k → 0.1%, etc.

10% is a lot, but nobody is going to care about that, because a project with 100 patrons isn’t making anything anyway (the first payout wouldn’t happen for 2.5 years). And, of course, those are the upper bounds; it should actually average to half that.


On the other hand, what do you do in the following scenario?

  • I support two projects, A and B
  • I am the only patron of both projects
  • Project A has 20 other patrons
    • Total income = 2.1¢ * 21 patrons = 44.1¢
  • Project B has 18 other patrons
    • Total income = 1.9¢ * 19 patrons = 36.1¢

To make mental math easier, let’s assume we do payout every month. We’re not losing anything doing this since I could easily make an equivalent example where the patron numbers are high enough to put things above the threshold. So, the first payout happens.

  • Other patrons of project A are paying 2.1¢
    • They (20) are each charged 2¢ with 0.1¢ carried over → 40¢ total
  • Other patrons of project B are paying 1.9¢
    • They (18) are each charged 1¢[1] with 0.9¢ carried over → 18¢ total
  • I am am charged 1.9¢ + 2.1¢ = 4¢, with no carry-over.

How do we divvy up the funds?

  1. Sum all the charges and then divide them proportionately.

    • We charged patrons a total of 61¢.
    • Un-rounded project total income is 44.1 + 36.1 = 80.2¢
    • Project A is 44.1/80.2 = 0.5498… and B is 36.1/80.2 = 0.4501…
    • Multiplying those fractions by 61¢ yields 33.54…¢ and 27.45…¢.

    Problems with this method:

    • We can’t actually do the full payout to projects without rounding.
      • In this example, we’d have to give project A 34¢ and project B 27¢.
    • Projects’ income consistency is affected by other projects. If project B didn’t exist, A would get 42¢ each month; instead,
      • In fairness, this is a much smaller concern as we get to amounts of money anybody cares about.
      • Also in fairness, this is the case regardless of payment method, because pledging to other projects affects when you hit the minimum charge.
  2. Maintain individual balances for each patron/project.

    • Project A receives 42¢ (40 from others; 2 from me)
    • Project B receives 19¢ (18 from other, 1 from me)
    • Note: in this scenario, I get charged 3¢, not 4¢, with a pending balance of 0.1¢ to project A and 0.9¢ to project B.

    Problems with this method:

    • I have a pending donation (of 1¢) that could be charged but isn’t. This could be confusing to show & make our dashboard more complicated.

Also, with any scenario where we allow pledges to happen in fractions of a cent, we can have “infinitely pending donations”, when a patron decides to stop pledging and their outstanding balance is in fractions of a cent. This isn’t a problem in terms of reduced income for projects, since it would be rounded down otherwise, but it does create a scenario where projects have might believe they have pending donations, that they will never actually receive.


Fractions of a cent are fine. We use them in the real world for things like gas prices, as wolftune mentioned. But that works precisely because purchases are rounded to the nearest cent at the time they’re made, not on an ongoing basis.

I agree that the code would be simpler/better if we never rounded, but I’m not convinced this is worth the trade-off in real-world complexity. Do you know of any examples of repeated, fractional-cent, non-refundable transactions which are charged in aggregate? That would go a long way towards convincing me it’s a good choice.


  1. rounding up to 2¢ would be really nice here, but overcharging people when we are explicitly not rounding seems like a legal recipe for disaster, though maybe a lawyer would feel differently and everything would be OK. ↩︎

@chreekat this is an intriguing point that hadn’t occurred to me, but do you feel there’s a realistic concern that patrons would get upset about this? Even if we always rounded up, that would only affect how much each patron donated to each project each month by at most one cent, compared to the other extreme of always rounding down. It still seems to me that we should follow the standard of rounding 0.5 or higher up and below 0.5 down on the back end, so we never have any fractions of cents represented anywhere in the system.

Another option is just to have $0.01 be the smallest unit in the calculation. that way, we never have to round. I think its also more transparent, and easier for people to conceive / understand. Everyone can visualize a penny.

Reading through @smichel17’s post, I realized something that makes me much inclined to agree we should just use cents. We don’t want eventual consistency between charges and donations, we want instantaneous consistency. I think Stephen overlooked elements of the mechanism that do ensure consistency, but that consistency is only eventual, because payments and donations are processed separately. We’d be hanging on to people’s money (pennies, to be sure) until the next round of donations.

Anyway, the important point is that I’ve come around. We have to round something, somewhere, in order to have instantaneous consistency.

Unfortunately, that doesn’t lessen the risk of screwup that rounding entails. Let’s think about this some.

2 Appreciations

I don’t like this trade-off, but it’s always possible to simply say “1¢ for every 10 patrons” even though it’s not ideal in that each new patron isn’t strictly matched (we’d have recurring 10-patron thresholds), and each pledge doesn’t immediately increase the instantaneous crowdmatch amount from everyone else.

Another option to consider: round down at the charge point, just carry over the fractions of a cent. This leaves some money on the table, so to speak, but it’s just pennies. In this case, there’s no rounding even at crowdmatch points, everything just adds up until a patron has a balance large enough to charge, but we carry-over the fraction of a cent to the next future charge.

The weirdest challenge would be managing how that would work when a charge includes donations to multiple projects.

“Just Pennies” could develop into large sums of cash with multiple projects. At what carry-over threshold are we legally considered to be money-handleing and no longer just a payment coordinator?

I don’t think there’s any scenario in which Snowdrift succeeds in its mission of changing the landscape of FLO funding and the pennies lost are of any consequence.

I’m neither a lawyer nor the person who researched this issue, but as I understand it, we would not have to worry about it here, since “carried over” here is talking about the pending donation, not something we charged patrons but didn’t give projects.

Excellent point. However, I’m still leaning more towards using just pennies. It’s simpler, easier to understand, easier to explain/sell/market, and easier to program.

However, if we were to switch to pennies instead of 1/10 of pennies as the starting unit, what are the dis-advantages? What is lost by having a higher base unit?

It quickly becomes unaffordable, especially for people of modest means. I don’t know anybody who’d donate $100/mo to a single project. $10/mo from 10 000 patrons is a lot more money than the same amount from 1000 patrons.

1 Appreciation

Vaguely related anecdote: Even megacorp banking websites do rounding wrong. :stuck_out_tongue:

image

(Actual amount: $0.99)

2 posts were split to a new topic: Adjust the match base to 1¢?

I’m returning to this post as I drill through some of my todo items.

I think this post opened up the “multiple projects” can of worms, which we are explicitly ignoring right now until we get the rest of the site in a sensible state (operationally, design-wise, and so forth). I.e. this post combines a real, current, actual problem (the inaccurate dollar amounts) with some other topics that we should avoid spending time on until we are ready to fully tackle supporting multiple projects. I suggest we solve the former and let the latter rest for now.

For solving the inaccurate dollar amounts, having heard all arguments, I simply think it is best to show tenths of a cent. It is the easiest fix, and creates no new tradeoffs. It obviously won’t be the final solution, but it is sufficient to solve the immediate problem.

I’ve opened https://git.snowdrift.coop/sd/snowdrift/issues/113.

3 Appreciations

Do you mean show tenths of a cent for the actual crowdmatch amounts, but just cents for the charge transactions? So we’d defer rounding until an actual charge? I don’t think it would be OK to show tenths of a cent on the actual charges. Also, when showing them, maybe we could display the third decimal place in gray or something to minimize confusion? (@mray what do you think?)

We are not technically able to charge a 10th of a cent, as our current payment processor, stripe, only allows amounts in the smallest currency unit for whatever currency unit we are charging.
Source.

The more ethical approach would be to round down, to the nearest cent, and then carry over the amount above the nearest cent to the next payment event. What @chreekat is suggesting is that for maximum transparency, the tenths of a cent will be shown on your transaction page, so it’s clear what is happening with your money.

I think we should go with the solution that creates the least problems (whatever devs prefer) and have a good idea how we want it in the end. I can imagine a solution like always rounding down and showing the precise amount on mouseover.

I share @msiep s interest in what the context here is: would this solution be applied anywhere?

As I posted above with image of gas station prices, it is a good idea to make the 3rd decimal visually distinct so it doesn’t confuse people who rarely see dollars with 3 decimals. But indeed, there are ways to do it. I think the fraction like the gas station is better than just grey.

I agree, if we can do that. I’m not sure it would be feasible though since although I know character sets can include a few fractions like ½ and ¼ I don’t suppose there are characters available for 0/10 through 9/10?

1 Appreciation

Sorry for not being clear. The immediate, accurate, and simple-to-implement solution is to show tenths of a cent for crowdmatches and full cents for donations. (Except that we don’t even show donation amounts anywhere yet, so it’s only crowdmatch amounts that matter today.)

In other words: to fix the bug, we show tenths of a cent for crowdmatches today. Making the site easier to grasp – whether with visual cues, changed calculations, or whatever – is still out of scope for this bug.

3 Appreciations

Thinking about this again since I just opened #939 - Store Crowdmatch amounts as cents - snowdrift/tasks - Codeberg.org to track the rounding-at-crowdmatch-time that I proposed several years ago.

I agree this is a significant concern. It’s a much more rewarding experience when your pledge is matched immediately, instead of delayed in 10-patron increments.

Here’s a compromise idea: **

Benefits:

  • We can still truthfully show matching happening for each patron that pledges
  • We can zero out a patron’s balance every time they are charged
  • We have another source of income into the fund besides “someone generous donates to it” or out of Snowdrift’s own patronage.

Problems:


  1. It needs to be down for the unlikely scenario where there are more projects than patrons. In that case, the total charged to patrons can be less than the amount owed to projects. ↩︎