## digitalmars.D.learn - Removing the precision from double

• kerdemdemir (21/21) Nov 01 2018 I have two numbers
• H. S. Teoh (27/38) Nov 01 2018 Beware! Floating-point values in D are stored as binary, and certain
• Nicholas Wilson (13/35) Nov 01 2018 the function you does what you describe is called quantize.
• Neia Neutuladh (57/62) Nov 01 2018 tldr: consider using std.experimental.checkedint and highly granular uni...
```I have two numbers

First The price  = 0.00000016123
Second Maximum allowed precision = 0.00000001(it can be only
0.001, 0.0001, 0.00001, ..., 0.0000000001 bunch of zeros and than
a one that is it)

Anything more precise than the allow precision should truncated.
So in this case 0.00000016123 should turn into 0.00000016.

I coded this which in my tests have no problem :

double NormalizeThePrice( double price, double tickSize)
{
import std.math : floor;
double inverseTickSize = 1.0/tickSize;
return floor(price * inverseTickSize) *  tickSize;
}

writeln(NormalizeThePrice(0.00000016123, 0.00000001));
Returns 1.6e-07 as excepted.

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 ?

Erdemdem
```
Nov 01 2018
```On Thu, Nov 01, 2018 at 11:59:26PM +0000, kerdemdemir via Digitalmars-d-learn
wrote:
I have two numbers

First The price  = 0.00000016123
Second Maximum allowed precision = 0.00000001(it can be only 0.001,
0.0001, 0.00001, ..., 0.0000000001 bunch of zeros and than a one that
is it)

Beware!  Floating-point values in D are stored as binary, and certain
decimal fractions cannot be stored exactly in binary.  Because of that,
it's generally better to consider using a decimal arithmetic library for
representing monetary amounts, especially when you have precise
requirements on the number of decimal places a number has to fit into.
IEEE floating-point has some quirks that can give you a nightmare of a
debugging session if you're expecting them to behave exactly like
decimals.

[...]
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 ?

[...]

You probably want to be using a decimal number library instead of
floating-point. The mismatch between the binary representation and the
decimal representation will give you a headache especially if you're
dealing with financial transactions that expects certain behaviours of
the decimal digits, which the binary digits cannot exactly match.

Either that, or store your numbers as fixed-point integers (e.g., as an
int or long with the rightmost n digits interpreted to be the fractional
part -- if you go this route you'd want to encapsulate the arithmetic in
a struct with overloaded operators so that it's more convenient to use,
and you'll want to be careful of arithmetic overflows).

Using IEEE floating-point for financial computations is tricky business,
and you need to fully understand what you're doing and exactly how IEEE
floats work, in order not to get yourself into trouble at some point.

T

--
We've all heard that a million monkeys banging on a million typewriters will
eventually reproduce the entire works of Shakespeare.  Now, thanks to the
Internet, we know this is not true. -- Robert Wilensk
```
Nov 01 2018
```On Thursday, 1 November 2018 at 23:59:26 UTC, kerdemdemir wrote:
I have two numbers

First The price  = 0.00000016123
Second Maximum allowed precision = 0.00000001(it can be only
0.001, 0.0001, 0.00001, ..., 0.0000000001 bunch of zeros and
than a one that is it)

Anything more precise than the allow precision should truncated.
So in this case 0.00000016123 should turn into 0.00000016.

I coded this which in my tests have no problem :

double NormalizeThePrice( double price, double tickSize)
{
import std.math : floor;
double inverseTickSize = 1.0/tickSize;
return floor(price * inverseTickSize) *  tickSize;
}

writeln(NormalizeThePrice(0.00000016123, 0.00000001));
Returns 1.6e-07 as excepted.

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 ?

Erdemdem

the function you does what you describe is called quantize.

However be warned that dealing with money as a floating point
type is generally not a great idea. Precision losses (especially
when working with ranges like 10^-7) and NaNs (Sociomantic's \$9T
bug, yes 9T thats what lround(NaN) gives)  are thing you
_must_ account for.

I _think_ the accepted way of dealing with it is to use an (non
overflowing!) integer representing the lowest amount (e.g. 1
cent), and deal in integer multiples of that.

: https://dlang.org/phobos/std_math.html#.quantize
: https://dconf.org/2016/talks/clugston.html
: https://dlang.org/phobos/core_checkedint.html
```
Nov 01 2018    Neia Neutuladh <neia ikeran.org> writes:
```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.

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.

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 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.

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.
```
Nov 01 2018