Closed maaku closed 11 years ago
Having thought about it some more, a clear downside is that coin values can no longer be displayed exactly in the user interface, or specified by the user. At least with a power-of-10 COIN
displayed values are exact, or derived according to a clear, unambiguous rounding method.
Representing 2/3
as exactly 66666667 satoshis at least forces an explicit choice about how to deal with non-base-10 representable amounts.
Any rounding is unambiguous. The second-best pick would be 10,485,760,000 - at least that's got a lot of 2s.
Of course, the even better solution would be to store values as fractions; ie, a varint numerator and varint denominator. Then there's true infinite divisibility and no problem with primes. This DOES really make rounding ambiguous, but IMO not really a problem (it wasn't with Bitcoin for the first 2 years at least - until I got on the project in 2011, wxBitcoin rounded everything to 2 decimal digits)
Mark was working on a fractional approach to solve this issue: https://github.com/freicoin/freicoin/issues/3
But finally a refHeigh field was added to transactions. This was about fractions of previous outputs tracing back to the origin of the coins. Do you mean fractions of a coin? That would be much simpler.
Fractional amounts make more sense to me, but I suppose the same thing could be implemented (in a rather indirect and blockchain-bloating way) using fractions of coins.
@luke-jr is talking here about changing how many satoshis represent 1 FRC, mostly for the purposes of user interface, which is a separate issue from the mechanism for demurrage calculation.
However that does bring me to the observation that we've effectively standardized on quadruple-precision floating point for the purposes demurrage calculation (with platform-independent arithmetic and rounding modes provided by the MPFR library). One way to achieve better divisibility would be to store coin amounts as floating point values as well (double-precision, most likely). Technically you'd actually lose some ability to do exact splits as floating point is a binary fraction, but you'd gain much better precision and effectively infinite divisibility.
Using floating-point does introduce all sorts of subtle issues that you'd have to be careful about. All deterministic/protocol calculations would have to be done through the MPFR library, and since we'd be reading/writing floating point from the blockchain, we'd need to do some extra validation--checking specifically for subnormal values, not-a-numbers, infinities, and positive vs. negative zero.
It would also clearly break compatibility with bitcoin transactions and existing blockchain-reading utilities. Currently freicoin and bitcoin transactions are basically the same, but with the nRefHeight field appended to the end of nVersion=2 freicoin transactions, which is a simple enough change for utility writers to support. It could also theoretically be added to bitcoin (where the nRefHeight field would have little meaning) to ensure widespread support, but I doubt anyone with a commit bit would agree to that anytime soon... but with floating-point amounts it is exceedingly unlikely that such a transition would ever occur.
However the clear advantage would be that all values, big or small, would be rounded to approx. 16 bits of decimal precision. I don't think anyone would care that they got 0.3333333333333333 coins instead of 0.3333333333333334.
There's a general rule to never use floating-point for currency. Maybe it could be justified in some cases, but even if so, the PR from doing it will likely kill it before it's considered.
That general rule exists because 1) the inherent gotchas concerning all forms of floating point arithmetic which I dealt with above (determinism, rounding modes, precision, etc.), and 2) fiat currencies are decimal; values can't be represented exactly, and modes like “round-to-nearest” on the same number doesn't have the same effect in decimal vs. binary.
Addressing the first set of issues is simply a matter of correct and careful software engineering. That's why libraries like MPFR exist.
The second issue is a bit more fundamental, although that's explicitly why decimal floating point standards exist; using decimal floating point would certainly be one approach. Since we're creating a new currency, we also have the option of defining binary arithmetic as canonical, in which case the situation would be oddly reversed. However as long as humans are involved decimal arithmetic would be preferable.
As for the PR issue, I'm not really concerned. The full rule is “don't use floating point for currency calculations unless you really know what you're during.” I have a background in numerical analysis and computational science--I know the issues involved and understand the consequences of our actions. I'm sure you have similar qualifications. We know what we're doing.
Still, I'm just throwing it out there as an option. I'm not really sure what approach, if any, is best here.
Obviously I'm opposed to anything bringing decimal down to a low-level.
Hrm.. I recognize that (decimal) floating-point arithmetic doesn't solve the original issue, although in my mind it obviated the problem (with guaranteed 16 decimal places of representation, who cares if a split is approximated? send the extra “satoshi” to the miner as a fee). I'll create a separate issue for making the serialization format of values to be decimal floating-point instead of int64.
Rather serialize as fractions still (varint numerator + varint denominator). If FP is preferable, I wonder if perhaps base 30 (2_3_5) FP would cover all practical use cases?
I have opened a new issue regarding migration to the IEEE-standard decimal64
for CTxOut::nValue
. This would solve, I feel, the primary practical issue I see in this discussion over divisibility: namely the possibility that 8 decimal places of granularity may not be enough in the distant future or for non-standard uses of the currency.
For the other issue of non-base-2 or -base-5 exact divisibility, I do not feel the use case has been adequately explained. Freicoin is inherently noisy with balances being reduced by 2**-20 every ten minutes, meaning that 1ULP of a decimal64 is lost in a matter of seconds or less, on average. So it is not clear to me that a situation exists where exact divisibility would be required.
In the absence of a clear use case, I am closing this issue.
decimal64 has limited precision, though I suppose you can workaround it with multiple outputs. Base 2 and base 5 should be (somewhat) fine with it otherwise (you're limited to 8 digits of base 5, 32 of base 2, and 8 of tonal). But for example, 123,4567,8123,4567 tonal (81,985,529,073,321,319 decimal) would not fit in decimal64. There is also no benefit to using decimal64 over proper fractions.
This was suggested by @luke-jr on IRC. Specifically, @luke-jr suggested the constant
3317760000
, which is equal(5**4) * (3**4) * (2**16)
. This would allow sending "one third of a coin" exactly.If we went down this route, I would suggest
9953280000
, which is(5**4) * (3**5) * (2**16)
, as this is within half a percent of a power of 10, making mental math and back-of-the-envelope calculations easier.