On Thu, 01 Nov 2018 23:59:26 +0000, kerdemdemir wrote: > I am doing trading and super scared of suprices like mathematical errors > during the multiplications(or division 1/tickSize) since market will > reject my orders even if there is a small mistake. > > Is this safe? Or is there a better way of doing that ?
tldr: consider using std.experimental.checkedint and highly granular units, like milli-cents. Also consider using a rational number library. Doubles aren't necessarily bad, but they'd concern me. # 1. Fixed precision The common way of working with sensitive financial stuff is fixed precision numbers. With fixed precision, if you can represent 0.0001 cents and you can represent ten billion dollars, you can represent ten billion dollars minus 0.0001 cents. This is not the case with single-precision floating point numbers: writeln(0.0001f); writefln("%20f", 10_000_000_000f); writefln("%20f", 10_000_000_000f - 0.0001f); prints: 0.0001 10000000000.000000 10000000000.000000 The problem with fixed precision is that you have to deal with overflow and underflow. Let's say you pick your unit as a milli-cent (0.001 cents, $0.00001). This works okay...but if you're trying to assign a value to each CPU cycle, you'll run into integer underflow. Like $10/hour divided by CPU cycles per hour is going to be under 0.001 cents. And if you try inverting things to get cycles per dollar, you might end up with absurdly large numbers in the middle of your computation, and they might not fit into a single 64-bit integer. ## Fixed precision with Checked std.experimental.checkedint solves the overflow problem by turning it into an error. Or a warning, if you choose. It turns incorrect code from a silent error into a crash, which is a lot nicer than quietly turning a sale into a purchase. It doesn't solve the underflow problem. If you try dividing $100 by 37, you'll get 270270 milli-cents, losing a bit over a quarter milli-cent, and you won't be notified. If that's deep in the middle of a complex calculation, errors like that can add up. In practice, adding six decimal places over what you actually need will probably be good enough...but you'll never know for certain. # Floating point numbers Floating point numbers fix the underflow and overflow problems by being approximate. You can get numbers up to 10^38 on a 32-bit float, but that really represents a large range of numbers near 10^38. **That said**, a double can precisely represent any integer up to about 2^53. So if you were going to use milli-cents and never needed to represent a value over one quadrillion, you could just use doubles. It would probably be easier. The problem is that you won't know when you go outside that range and start getting approximate values. # Rational numbers A rational number can precisely represent the ratio between two integers. If you're using rational numbers to represent dollars, you can represent a third of a dollar exactly. $100 / 37 is stored as 100/37, not as something close to, but not exactly equal to, 2.702 repeating. You can still get overflow, so you'll want to use std.experimental.checkedint as the numerator and denominator type for the rational number. Also, every bit of precision you add to the denominator sacrifices a bit of range. So a Rational!(Checked!(long, Throw)) can precisely represent 1/(2^63), but it can't add that number to 10. Manual rounding can help there.