Using floating-point numbers for money
evanjones.ca
evanjones.ca
Also, I wonder what the overhead of rounding every operation is? Comparable to the cost of using a proper Decimal class?
"if you are doing some financial math that does not need to be accurate to the penny, just use floating point numbers."
I would argue that financial math by definition needs to be accurate to the penny. Where is "pretty close" financial calculations considered acceptable? Having worked at a bank, I know how seriously this sort of thing is taken.
From experience, working in scientific applications and numerical computing, summing large numbers of floats is fraught with accuracy problems too.
Computing tips, from the standpoint of a customer paying the tip.
Saying "pay 20%" isn't precise to begin with, in that you will not use fractional cents to make it precise, so having a tipping granularity a bit larger than a penny is also acceptable, particularly in jurisdictions where there is no one-cent coin (or equivalent) in any event.
There is a difference between analysis and accounting. There are many financial models (e.g. Black-Scholes-Merton option pricing) that are analytic in nature and use transcendental functions, so the idea of getting an exact, arbitrary-precision answer is hopeless. Using a decimal type for this kind of computation would be an exercise in slowing down processing by a few orders of magnitude.
When computing around money, you're either working with magnitudes or units.
If you don't know which, you're working with units; use a Decimal.
Yes, indeed. There is no such thing as "financial math that does not need to be accurate to the penny". I wonder if OP has ever worked with a bookkeeper...
If you have some routines for dividing fixed-point numbers, one, why do you believe they have more accuracy than floating point (especially if you're doing divisions by numbers that aren't divisible by 10, as in the example above - don't you have the same problem as with floating point?), and why do you believe they're more correct than floating point? Did you write a test suite? Do you know what needs to be tested? What prevented you from writing the test suite for the floating-point calculations you were originally doing to do?
Fixed point is one answer, but I see you know that already. I don’t know what the banks use for interest, but I can guarantee that it’s not float32.
> If you have some routines for dividing fixed-point numbers, one, why do you believe they have more accuracy than floating point
Fixed point routines do not have more best-case accuracy than float, given the same number of bits ... but float32 definitely has a worst-case accuracy that is very very bad compared to a fixed point number.
> why do you believe they’re more correct than floating point?
Can’t speak for the GP, but I think asking about correctness is a straw man. The issue is really about safety, predictability, and controllability. Floating point can be very accurate, but guaranteeing that accuracy is notoriously difficult, and it depends very much on the unknown ranges of your intermediate calculations. Fixed point, on the other hand, never changes accuracy as you go, so you don’t get surprises.
Uh, and compliance? (if you didn't mean to imply that under "controllability")
It sometimes needs to be accurate beyond the penny; pennies may be the smallest unit of settlement, but they aren't the smallest tracked quantity in all financial matters.
For what it’s worth, for the major ICs (Intel, NVIDIA, etc.) there is zero extra overhead. A choice of rounding modes is part of the floating point operation’s instruction. And keep in mind that a floating point op is always rounding no matter what you do, the question is whether it’s always using the same rounding strategy consistently, whether you can control it, and whether it has what you need.
> I remain skeptical
You are correct.
There are good reasons not to use float for money that the article didn’t discuss, and perhaps the author isn’t aware of. You run out of integer precision at 2^24, which is only 16 million. If you process a 20 million dollar payment in units of dollars, you might be off by at least a dollar. That error will multiply with every floating point op done on the result. If your units are pennies, the largest safe value is only 160,000 dollars. If you ever subtract floats, like say make a payment or withdrawal, you can run into catastrophic cancellation without knowing it. Deposit $200k and then withdraw $199k, suddenly you have a small balance with large error that could remain in your account and continue to grow until the balance is zero. https://pharr.org/matt/blog/2019/11/03/difference-of-floats....
To be fair, the author seems to be suggesting using double-precision floating point. If you use integer numbers of pennies, signed 32-bit integers cap out at a similar value of $21M. 32 bits is just too small for financial calculations.
I'm a little more curious on the suitability of using integers for money though (integer number of pennies). I suppose there are cases (and rules) concerning fractional pennies? Like in the case of percents of interests, often given by the year, accumulated[1] by the month?
[1] that's probably not the correct term in English.
GPU engineers might have something to say about that. Depending on the context, trading off on precision can be a valid engineering choice.
The gist: It's efficient (adds and mults in a few instructions), and preserves the decimal representation.
It's quite simple, really: Store a whole-number integer with a smaller one representing the number of shifts to the decimal point. This is probably a good choice for sensitive financial calculations.
Most professional financial packages use fixed point decimal, which can easily be implemented by specifying a fractional unit (such as thousandths of cents). The computations will be faster (because they're ints) and at 64 bits you get a min/max value of +- 9,223,372,036,854,775,807 units (90 quadrillion if you're using thousandths of cents), and rounding functions will always be exact (provided you have enough digits of scratch precision).
There's also the 2008 addition of decimal floating point types to ieee754 [1], which have been implemented in gcc and clang (software emulation only).
So no, don't use binary floats for financial calculations. You're 99.9% guaranteed to get it wrong and introduce bugs.
[1] https://en.wikipedia.org/wiki/Decimal64_floating-point_forma...
Floating point is fine, so long as it is big enough for the application and decimal (not binary) floating point. Fixed point decimal is easier to get right, and, really, arbitrary precision decimal (or, for even more generality, arbitrary precision rational) is even easier to get right.
PSA: If your favorite language doesn't support ieee754 decimal float, start badgering them to add it! This has gone on long enough, and the inertia against change is strong.
Without hardware support (which makes decimal float a good choice for performance), I'm not sure I've ever run into a use case where decimal float is a compelling choice if I already have arbitrary precision decimal and hardware-backed binary float types available.
Generally, whatever you think you are getting from decimal floating point, you very, very likely are not.
There is exactly one place where decimal floating point is the Right Thing: when interoperating with another system that is already using it. Those exist because others' superstitions have been locked in, sometimes even into regulatory frameworks. Decimal floating point is inherently less accurate, on any lossy computation, than binary, so you need a lot more digits to maintain the same result accuracy. This is why 128-bit decimal is common.
It is generally pointless to argue with anybody who thinks decimal computations are better. If reason mattered to them, they wouldn't be stuck on the idea. So, just roll your eyes and, if they have any authority, use a library. Performance will suck, but not so badly as you might guess, and you can spend the time until release circulating your CV without panic.
https://docs.microsoft.com/en-us/dotnet/csharp/language-refe...
[0]: https://developer.apple.com/documentation/foundation/nsdecim...
[1]: https://developer.apple.com/documentation/foundation/nsdecim...
How coincidental that this should show up on the front page at the same time as the thread on normalization of deviance in software: https://news.ycombinator.com/item?id=22144330
What is wrong with using a currency library?
Do currency libraries exist for example for GPUs? (Maybe they do.)
That was my point.
If you’re programming a point-of-sale system, or a ecommerce site or something, using IEEE-754 floats would be madness. The increase in performance compared to decimal types is absolutely infinitesimal, and the cost of getting it wrong is MASSIVE. Just don’t do it!
Of course, if you really do need the performance advantage floats offer (e.g. high-frequency trading or machine learning), then by all means, use floats. But that’s a tiny minority of those programmers who have to deal with money.
The tax rate (like most things associated with money) should be stored as an exact decimal (fixed point decimal, decimal floating point, or arbitrary precision decimal all work.)
> I guess you need to calculate the tax for each item, round it to cents (and I assume the rules on how you round can vary by jurisdiction, so it's not just calling your language's round function), multiply that by the quantity of that item (good luck if the quantity can be a decimal value), then sum each line item.
Usually taxes are required to be calculated by summing the pre-tax cost of all line items, applying the tax rate, rounding by the applicable rules, and then adding that to the pre-tax subtotal to get the final total. But having decimal unit prices (even decimal cents, e.g., gas is usually priced in mils, not cents), decimal quantities, and decimal tax rates are all non-problems (so no "good luck" needed) as long as you are using an exact decimal representation rather than binary floating point.
In Italy the generic VAT rate is 22%.
When you expose a price (to the final customer) it is always included VAT, but VAT needs to be explicited on fiscal documents (receipt and/or invoices).
So you have an article priced to the public Euro 2,69 (included 22% VAT), what is the net price and how much is the VAT? 2,69/1,22=2,204918...
So, let's try rounding it to 2,20: 2,20x0,22=0,484=0,48 no, wait, 2,20+0,48=2,68
Ok, let's try rounding it to 2,21: 2,21x0,22=0,4862=0,49 no, wait, 2,21+0,49=2,70
You have to use the first and add 0,01 to the VAT to have it work:
2,20+(0,48+0,01)= 2,69
Great, how do you become confident that your hand-rolled decimal type is less buggy than the known hazards of using floating-point?
C#, Ruby, Python, and Java, at least, among industrially popular languages all provide a fixed and/or arbitrary precision decimal type in either the core language or stdlib. Lower level languages sometimes don't, and JavaScript, among higher-level popular languages, notably does not.
In C:
unsigned long tax_in_cents(double amt, double rate)
{
/* requirement: amt is an integer multiple of 0.01
and rate is an integer multiple of 0.00001 */
const unsigned long M = 10000000;
return (unsigned long)round((round(amt * rate * M) / (M/100)));
}
That should work, at least for amt that isn't too high.Here is how I would do it, using integers:
unsigned long tax_in_cents(unsigned long amt2, unsigned long rate5)
{
/* amt2 is the amount in cents, rate5 is tax rate * 10000 */
return (amt2 * rate5 + 50000) / 100000;
}
(My convention in this kind of code is that if an underlying thing is represented as an integer by multiplying it by a power of 10, I put that power as a suffix on the integer variable name. So amt2 means the real amount x 100, and rate5 means the tax rate x 100000. A ...2 x ...5 gives something that is ...7, which is why we divide by 10^5 to get the tax in cents (a ...2). Half of the divisor, or 10^5/2, is what you add for rounding).I've not proven the above floating point code, but I have brute force tested it with every combination of rates from 0.00001 to 0.99999, price from $0.01 to $10000.00, and floating rounding modes FE_UPWARD, FE_TONEAREST, and FE_DOWNWARD.
Most initial attempts to write such a function make the mistake of doing the rounding at the wrong place. They just try something like amt x rate then round that to the nearest cent.
The problem with that can be seen by a very simple example: 10% of $21.15. It should be $2.12. It comes out before rounding as 2.1149999999999998, so rounding to the nearest 0.01 comes out as $2.11. Various "obvious" ways you might do this might work for that case, but they fail in others. For example, here are three ways in Python that readily come to mind:
def tax_f1(amt, rate):
tax = round(amt * rate,2)
return round(tax * 100)
def tax_f2(amt, rate):
return round(amt*rate*100)
def tax_f3(amt, rate):
return round(amt*rate*100+.5)
but try all of those on these: 1% of $21.50
3% of $21.50
6% of $21.50
10% of $21.15
and every one of them fails at least once. The correct answers 22, 65, 129, and 212. Here is what they give, with the wrong ones prefixed with !: tax_f1: !21 65 129 !211
tax_f2: 22 !64 129 !211
tax_f3: 22 65 !130 212
There was a discussion of this here a couple months ago [1]. In that discussion, someone who knows considerably more about floating point that I do (kccqzy) suggested how to fix it, and even his first try failed when I run it through my brute force testing. He then suggested a method that worked (at least according to my brute force testing).> One piece of popular programming wisdom is "never using floating-point numbers for money." This has never made sense to me.
He then goes on to explain why it absolutely does and must make sense. It honestly seems like the author's entire point is about not accepting blanket statements.
> My summary: if you are doing some financial math that does not need to be accurate to the penny, just use floating point numbers.
Arguably all financial math needs to be accurate to the penny. I can't help but think he is maliciously trolling, trying to convince people of something that isn't true.
Of course, if you can accept rounding errors, it is fine. But in many monetary applications, that is not OK. The article is quite pointless.
Edit. For example, when doing budget forecasts, the result will never be accurate to the penny. It is then OK. But when doing accounting, you need to account for every penny. Not OK.
My opinion: just NO.
MULTIPLE failures to understand the problem have occurred in this article, and they deal with representation.
A) It is never acceptable to 'round up' on interest payments/loans/debts/balances, without having an explicit line item for why. "Because compiler decision made by programmer" is indefensible.
B) Representation is EVERYTHING. Case in point:
>>>>0.1 + 0.2: Produces 0.30000000000000004 but should be exactly 0.3.
This statement can kill.
C) Standards are there to keep everyone, and everything, 'honest', and by honest it means: everyone understands things implicitly. Explain to grandma why a rounding error has resulted in her teeth falling out, or GTFO.
D) Fixed point is a standard representation. Its not about the precision-fail, its about if everyone gets exactly the same results, every time, in doing the math.
E) Lots and lots of these bugs yet to be fixed.
No, the solution is not to use floating point numbers to store money.
I worked on an iOS app in fintech for years, and let me assure you, using floating point numbers to calculate currency is an exercise in frustration and lack of correctness. When balances are wrong you're losing your customers money, which in turn loses trust in your product. You know it's inexact, an approximation, and your customers want an exact answer.
Just do it with a fixed-precision decimal number, and represent it as a string.
Why? If you are going to do that, might as well use an integer of cents. It's more compact and if you're worried about these calculations and storing this kind of information, it's likely you are storing lots of it (or it wouldn't be a problem).
I used to work in the online gambling industry for 15 years and there are a large number of use cases on a gambling front end where you do financial arithmetic.
I have seen equality between 2 floats being used as setting the end condition for a count up win animation. I don't think I have to explain what horrible thing that lead to.
> Unlike U.S. equity markets, which switched to decimal pricing in 2001, U.S. Treasury securities still trade in fractions. In particular, prices are quoted in 32nds of a point, where a point equals one percent of par, with the 32nds themselves split into fractions. On the BrokerTec platform, for example, 3- and 5-year notes trade in quarters of 32nds, whereas 7-, 10-, and 30-year securities trade in halves of 32nds. The quoted price for a 5-year note might be 98-15¼, for example, indicating a price in decimal form of 98.4765625 (that is, 98 + 15⁄32 + ¼⁄32).
[1] https://www.newyorkfed.org/aboutthefed/fedpoint/fed07.html
[2] https://www.bloomberg.com/opinion/articles/2020-01-15/it-s-n...
Eg. 117.125
> • Prices are quoted in 32nds of a dollar.
> [...] Note and bond prices are quoted in dollars and fractions of a dollar. By market convention, the normal fraction used for Treasury security prices is 1/32.
If you had a price of 1.25 then you could represent this as a float as (binary) 1.01. However if you have 117.25 then that's (decimal) 1.83203125 * 2^-6. You can see how you quickly run out of bits.
So, yes, you might get away with floating point values for financials -- and I have even seen banking code that did -- but that doesn't mean it's a good idea. Especially when libraries providing fixed point and arbitrary precision decimal representations are widely available and easy to use.
The biggest problem with using IEE 754 for currencies is that IEE 754 defines a domain and a set of operations that don't match the domain and set of operations required for currency calculations. The discrepancies between the "right" answers and the answers given by IEE 754 aren't because IEE 754 is in some way "wrong", it's because it's not calculating what you think it's calculating.
It’s great how coding style books rarely address these important things and focus only on bookshedding. Anyone know of a good presubmit checklist for semantic issues?
> if you are doing some financial math that does not need to be accurate to the penny
There is no such thing. I worked with applications that dealt with tens of billions of Euros, and if the bottom line was off by even 1 cent, the users would come to us to figure out what went wrong. Suggesting that this is acceptable to knowingly introduce errors, when there is a pretty well-established practice that allows to avoid them entirely, is baffling.
What does that even mean, “produce the nearest float with that number of digits?”
If my float representation says e. g., 2.6749999999999998, and I “tell” it to round to three digits, the result is still 2.6749999999999998 in floating point representation, isn’t it?
A floating point number doesn’t care about the number of significant decimal digits you want it to have.
$ python3
Python 3.7.3 (default, Jun 16 2019, 16:10:46)
[Clang 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 2.675 == 2.6749999999999998
True
So IF you have enough excess precision at every step, and IF you do the rounding right at each step, then you can get the same results using binary fractions as with decimal.Of course, correct rounding is still an issue when using a decimal type, since round(sum(n for n in ns)) ≠ sum(round(n) for n in ns).
This also happens with Javascript and Elixir, not just Ruby. So perhaps it has something to do with how the cpu stores the numbers?
I know this isn't StackOverflow, but could someone shed some light on to what's happening here? https://ideone.com/1U7rgf
Looks like that’s what’s going on with your example. Floating point approximation of an otherwise unrepresentable irrational binary which resulted in that repeating sequence when converted back to decimal again. I don’t know how BigDecimal works but I guess just by the name it is not floating point but rationals represented by arbitrary precision integer numerators and denominators.
Definitely read up on how floating point representation works, with binary mantissas and exponents, normalization, and converting decimal fractions to binary fractions.
Well, if you tell them to, e.g., by using a binary floating point type instead of a decimal type.
That is not the sense of "computers use binary" that justifies the conclusions in the sentence in which the phrase was used in the post being responded to, so while true on its own, in context it is an example of the fallacy of equivocation.
See, for instance, https://en.m.wikipedia.org/wiki/Decimal32_floating-point_for...
> it doesn't follow that you have binary fractions
Is that in reference to something I said earlier, or are we just falling deeper down a rabbit hole of hair splitting?
The point I was driving at is that a given number we think about in decimal like 12.3456 can be represented in binary in more than one way, one of which are rationals backed by integers for num/den, another is floating point with mantissa and exponent. Which way is chosen ultimately affects the outcomes of computations. I was trying to explain why OP was seeing unexpected results. I don't think I've been mistaken in the explanations...
So it's not "since computers use binary", but "since we use binary floating point". There are fair arguments that we made that choice because of efficiency concerns driven by the fact that "computers use binary", but I don't think that really helps the explanation.
Note that the original objection was picking at a nit that I am not sure I would have picked at, I am just seeking to clarify.
You cannot store 8.95 in a binary float (independent of precision). It is basically the same problem that you cannot store 1/3 as a decimal number.
What's really hard is printing a floating-point number. Since you have to print "8.95" when all you really have is a base-2 rational very close to it, you want to be sure you don't actually need to print 8.94999998. If you miss the last couple bits in the red part in Float Toy, you'll see the print algorithm shifts away from the 8.95 answer.
Assuming that the underlying implementation of Ruby uses sscanf for the conversion (I'm assuming Ruby is in C or C++?), that should work.
I had some concerns about this general issue, and did some brute force testing. The following program constructs strings "0.1", "0.2", ..., "0.9", "0.01", "0.02", ..., "0.99", ..., "0.99999999", uses sscanf to turn them into doubles, multiplies by the appropriate power of 10, rounds to the nearest integer, and checks if that is right.
#include <stdio.h>
#include <math.h>
int main(void)
{
int len = 1;
unsigned long div = 10;
char num[30];
while (len < 9)
{
for (unsigned long i = 1; i < div; ++i)
{
sprintf(num, "%.20lu", i);
num[19-len] = '.';
char * dp = num + 18 - len;
double d;
sscanf(dp, "%lf", &d);
unsigned long ci = round(d * div);
if (i != ci)
printf("%s, %lf, %lu\n", dp, d, ci);
}
++len;
div *= 10;
fprintf(stderr, "LEN=%d, DIV=%ld\n", len, div);
}
return 0;
}So... Don't use floats where accuracy is important? I agree.
No, it doesn't. It produces 0.3000000000000000444089209850062616169452667236328125.
> 1.40 * 165: produces 230.99999999999997
No, it doesn't. It produces 230.999999999999971578290569595992565155029296875.
> round(2.675, 2) produces 2.67
No, it doesn't. It produces 2.6699999999999999289457264239899814128875732421875.
> This is because 2.675 is actually 2.6749999999999998
No, it isn't. For one, 2.675 obviously is not 2.6749999999999998, but it also isn't converted to 2.6749999999999998 to be stored as a floating point number, it is converted to 2.67499999999999982236431605997495353221893310546875.
> round(2.665, 2) which produces 2.67
No, it doesn't. It produces 2.6699999999999999289457264239899814128875732421875.
> However, in floating-point numbers, it is above the halfway point (0.00500000000000000097)
Well, it is above the halfway point, but it's actually 0.0050000000000000009714451465470119728706777095794677734375.
> I am not a theoretician and have not proven that this is actually correct.
So, all the numbers are wrong, you have no clue whether your wrong results generalize, but you use that as the basis for advice to other people? I only can hope that you don't write software for other people to use.
There's no occurrence of the word count here https://en.m.wikipedia.org/wiki/Accuracy_and_precision.
We can count things and know that number exactly. We cannot measure anything to exactness.
We've all probably read that “Programs must be written for people to read, and only incidentally for machines to execute.”
When another (maintenance) developer sees float/double being used to represent money, their first reaction is going to be "uh oh, this dev didn't know what s/he was doing". There's a big risk of someone reflexively refactoring this later on.
You may ask, "What if I put in big comment blocks saying essentially 'I knew what I was doing here'?"
Well, even if maintenance devs read that and believe you, they're still likely to introduce errors, since they aren't used to using floats for money, and don't know all the tricks to avoid errors...
Unless there's some justifiable win in size / performance, this (although interesting) falls under my "Don't be too clever" rule.
In my experience one should do exactly the opposite, round/format only the very last result when you need to print it, otherwise always keep all the decimals in the intermediary results to avoid accumulating rounding errors as in a Superman 3 bank hack (aka "salami slicing"). I did a lot of affiliate and e-commerce software in my time and if you're careful you can use floats (sometimes you have to deal with old systems working that way), but fixed-point calculations with integers are way easier and safer. Just make sure to store enough extra decimal places, not just the 2 that you print and you can calculate interests, exchange currencies, charge 0.5% fee on $0.01 per click transactions, whatever - it works just fine.
FX is a different world. Spot FX trading systems are fully automated and compute rates to 5 DP. That was 15 years ago, I don’t know if it’s more now. Large Yen amounts can come worryingly close to the limits of precision.
I once watched a colleague trying to persuade a roomful of people to stop using floats. The demo showed what happens to 0.1 + 0.1 + 0.1. The answer was not 0.3. I’m not sure they believed him. Some of them are probably still writing software for your bank!
$ python3
Python 3.7.4 (default, Sep 7 2019, 18:27:02)
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> print (0.1 + 0.1 + 0.1)
0.30000000000000004
$ irb
2.5.1 :001 > print 0.1 + 0.1 + 0.1
0.30000000000000004 => nil
Chrome browser console:
> 0.1 + 0.1 + 0.1
< 0.30000000000000004
$ php
<?php
echo 0.1 + 0.1 + 0.1;
0.3This person was seriously thinking "this is just how computers are", and after a day of looking into it I was able to determine all the issues they were facing were floating point problems when the Windows front-end did it's calculations. It was pulling all the data directly from the old system, after all. How could it be different?
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... (it's the same in other languages)
- if results are not exactly reversible then you cannot implement verification and that hinders defensive programming
Floating point numbers were not created to represent numbers which could be non-integers. They were created to represent numbers in scientific notation. Very useful for physics and scientific calculations, but not in finance domains.
There are real monetary quantities for which this is insufficient. The GDP of Indonesia is measured in 10s of quadrillions of Rupiah. In cases of hyperinflation this kind of overflow can happen even for human-level accounting.
Use real fixed point decimal math.
Another potential issue is that the floating point rounding mode can be modified by shared libraries or plugins.
[0] has examples of application crashes caused by a change in the rounding mode.
https://stackoverflow.com/questions/582797/should-you-choose...
Also, you are going to end up storing in decimal fields in the database anyway.
why not use cents as the unit and store it without decimals?
That's a matter for the ETL that dumps the data on their screens.
What is faster in Postgresql? Double floats and TRUNC after each operation or using NUMERIC(precision, scale)?
Uh, no.
https://stackoverflow.com/questions/4467539/javascript-modul...
>JavaScript % (modulo) gives a negative result for negative numbers
>Question: According to Google Calculator (-13) % 64 is 51. According to Javascript (see this JSBin) it is -13. How do I fix this?
>Solution: ((i % n) + n) % n
The Forth-83 standard word /MOD (or FM/MOD) implemented floored division properly, subtly breaking many old FORTH programs, but it was a step (or rather a truncation) in the right direction, towards negative infinity instead of zero.
https://www.nimblemachines.com/symmetric-division-considered...
>Symmetric division considered harmful
>Since its 1983 standard (Forth-83), Forth has implemented floored division as standard. Interestingly, almost all processor architectures natively implement symmetric division.
>What is the difference between the two types? In floored division, the quotient is truncated toward minus infinity (remember, this is integer division we’re talking about). In symmetric division, the quotient is truncated toward zero, which means that depending on the sign of the dividend, the quotient can be truncated in different directions. This is the source of its evil.
https://forth-standard.org/standard/core/FMDivMOD
>Rationale:
>By introducing the requirement for "floored" division, Forth 83 produced much controversy and concern on the part of those who preferred the more common practice followed in other languages of implementing division according to the behavior of the host CPU, which is most often symmetric (rounded toward zero). In attempting to find a compromise position, this standard provides primitives for both common varieties, floored and symmetric (see SM/REM). FM/MOD is the floored version.
>The committee considered providing two complete sets of explicitly named division operators, and declined to do so on the grounds that this would unduly enlarge and complicate the standard. Instead, implementors may define the normal division words in terms of either FM/MOD or SM/REM providing they document their choice. People wishing to have explicitly named sets of operators are encouraged to do so. FM/MOD may be used, for example, to define:
: /_MOD ( n1 n2 -- n3 n4) >R S>D R> FM/MOD ;
: /_ ( n1 n2 -- n3) /_MOD SWAP DROP ;
: _MOD ( n1 n2 -- n3) /_MOD DROP ;
: */_MOD ( n1 n2 n3 -- n4 n5) >R M* R> FM/MOD ;
: */_ ( n1 n2 n3 -- n4 ) */_MOD SWAP DROP ;