Round doubles operations - c#

Suppose I have three doubles, a, b and c.
double a = 1.234560123;
double b = 7.890120123;
double c = a * b;
c = 9.740827669535655129
I want to work with numbers with only 5 decimal places. So if I round a and b using Math.Round(a, 5) and Math.Round(b, 5) I get:
double a_r = Math.Round(a, 5);
double b_r = Math.Round(b, 5);
a_r = 1.23456
b_r = 7.89012
double c_r = a_r * b_r;
c_r = 9.7408265472
But when I calculate c, I still get a number with more than 5 decimal places (this will happen in every multiplication, division, potentiation and similar operations). I could round all results in my code, but that's hard work that I want to avoid.
As I use c in other operations and the results of this operations in other ones, I don't want to round all the intermediate results every time to not propagate the error caused by undesired decimal places.
Is there a way to define doubles with a fixed number of decimal places, independently of the operation?

Typically, it's best to leave the doubles in place, and use the custom formatting to display the values to 5 decimal points:
double a = 1.234560123;
double b = 7.890120123;
double c = a * b;
Console.WriteLine("Result = {0:N5}", c);
Nearly all routines that convert numeric values into strings allow the use of the Standard Numeric Format Strings as well as Custom Numeric Format Strings.

You canĀ“t define a double with limited decimal places. You should rely on formatting the number when you display it. See this question

I found a way to solve my problem using Operator Overload.
So I re-defined all my operations as multiplication, division, complex multiplication and matrices operations to round the result to the number of decimal places I wanted.
An example:
public static double operator *(double d1, double d2)
{
double result;
result = Math.Round(d1 * d2, 5);
return result;
}

Related

Compare two doubles with different number of decimal places in C#

I want to compare two doubles a and b in C# (where for example b has more decimal places) in the way that: if I round the number b to number of decimal places of a i should get the same number if they are the same. Example:
double a = 0.123;
double b = 0.1234567890;
should be same.
double a = 0.123457
double b = 0.123456789
should be same.
I cannot write
if(Math.Abs(a-b) < eps)
because I don't know how to calculate precision eps.
If I have understood what you want you could just shift the digits before the decimal place til the "smaller" (i.e. one with least significant figures) is a integer, then compare:
i.e. in some class...
static bool comp(double a, double b)
{
while((a-(int)a)>0 && (b - (int)b)>0)
{
a *= 10;
b *= 10;
}
a = (int)a;
b = (int)b;
return a == b;
}
Edit
Clearly calling (int)x on a double is asking for trouble since double can store bigger numbers than ints. This is better:
while((a-Math.Floor(a))>0 && (b - Math.Floor(b))>0)
//...
double has epsilon double.Epsilon or set prefer error by hardcode, f.e. 0.00001

Double to Decimal without rounding after 15 digits

When converting a "high" precision Double to a Decimal I lose precision with Convert.ToDecimal or casting to (Decimal) due to Rounding.
Example :
double d = -0.99999999999999956d;
decimal result = Convert.ToDecimal(d); // Result = -1
decimal result = (Decimal)(d); // Result = -1
The Decimal value returned by Convert.ToDecimal(double) contains a maximum of 15 significant digits. If the value parameter contains more than 15 significant digits, it is rounded using rounding to nearest.
So I in order to keep my precision, I have to convert my double to a String and then call Convert.ToDecimal(String):
decimal result = System.Convert.ToDecimal(d.ToString("G20")); // Result = -0.99999999999999956d
This method is working but I would like to avoid using a String variable in order to convert a Double to Decimal without rounding after 15 digits?
One possible solution is to decompose d as the exact sum of n doubles, the last of which is small and contains all the trailing significant digits that you desire when converted to decimal, and the first (n-1) of which convert exactly to decimal.
For the source double d between -1.0 and 1.0:
decimal t = 0M;
bool b = d < 0;
if (b) d = -d;
if (d >= 0.5) { d -= 0.5; t = 0.5M; }
if (d >= 0.25) { d -= 0.25; t += 0.25M; }
if (d >= 0.125) { d -= 0.125; t += 0.125M; }
if (d >= 0.0625) { d -= 0.0625; t += 0.0625M; }
t += Convert.ToDecimal(d);
if (b) t = -t;
Test it on ideone.com.
Note that the operations d -= are exact, even if C# computes the binary floating-point operations at a higher precision than double (which it allows itself to do).
This is cheaper than a conversion from double to string, and provides a few additional digits of accuracy in the result (four bits of accuracy for the above four if-then-elses).
Remark: if C# did not allow itself to do floating-point computations at a higher precision, a good trick would have been to use Dekker splitting to split d into two values d1 and d2 that would convert each exactly to decimal. Alas, Dekker splitting only works with a strict interpretation of IEEE 754 multiplication and addition.
Another idea is to use C#'s version of frexp to obtain the significand s and exponent e of d, and to compute (Decimal)((long) (s * 4503599627370496.0d)) * <however one computes 2^e in Decimal>.
There are two approaches, one of which will work for values up below 2^63, and the other of which will work for values larger than 2^53.
Split smaller values into whole-number and fractional parts. The whole-number part may be precisely cast to long and then Decimal [note that a direct cast to Decimal may not be precise!] The fractional part may be precisely multiplied by 9007199254740992.0 (2^53), converted to long and then Decimal, and then divided by 9007199254740992.0m. Adding the result of that division to the whole-number part should yield a Decimal value which is within one least-significant-digit of being correct [it may not be precisely rounded, but will still be far better than the built-in conversions!]
For larger values, multiply by (1.0/281474976710656.0) (2^-48), take the whole-number part of that result, multiply it back by 281474976710656.0, and subtract it from the original result. Convert the whole-number results from the division and the subtraction to Decimal (they should convert precisely), multiply the former by 281474976710656m, and add the latter.

Does Math.Round() produce reliable hash values

Is it save to compare the result of Math.Round(double,int) with == and use it for example as the key of a HashSet<Double> or a GroupBy(d=>Math.Round(d,1))?
In other words, are there any doubles x and y for which the following assertion will fail?
double x = ...;
double y = ...;
double xRound = Math.Round(x, 1);
double yRound = Math.Round(y, 1);
Debug.Assert(xRound==yRound || Math.Abs(xRound-yRound)>=0.1);
Let's say that I would like to group a list of doubles:
List<double> values = ...;
List<double> keys = values.GroupBy(d=>Math.Round(d,1)).Select(kv=>kv.Key).ToList();
Is there a chance that I would get a key with the value 0.100000000 and another key with the value 0.09999999999?
(I tried parsing the disassembled net framework Math.cs source, but Round() eventually calls a native function.)
Generally, given the same starting value and the same operations, the same result would be obtained.
However, if you (for example) did:
double d1 = 10;
d1 /= 0.1;
double d2 = 25;
d2 /= 0.25;
then you may well find that d1 and d2 do not have the same value (because IEEE754 can represent 0.25 exactly but not 0.1.
So, given that and the rather large number of issues people seem to have with floating point, I'd say your best bet would be to choose a different method of hashing.

Math.Floor on the whole number

I am using Math.Floor method to find out how many times can number a be in number b. In this concrete example, variables are with these values:
double a = 1.2;
double b = 0.1;
double c = Math.Floor(a / b) * b;
This returns c = 11 instead of c = 12 as I thought it will. I guess it has something to do with rounding, but how can I get it work properly? When I raise number a to 1,21, it returns 12.
double and float are internally represented in a way that lacks precision for many numbers, which means that a may not be exactly 1.2 (but, say, 1.199999999...) and b not exactly 0.1.
If you want exact precision, for quantities that do not have a margin of error like money, use decimal.
Have a look here for how Math.Floor works. ..basically , it rounds down ...
My guess would be that with doubles you don't really get 12 as the division (even though this will seems to work: double d = a/b; // d=12).
Solal pointed to the same, and gave a good advice about decimal :)
Here's what happens with decimals:
decimal a = 1.2m;
decimal b = 0.1m;
decimal c = Math.Floor(a / b) ; // c =12
The result of your code is 1.1.
I am assuming you want to get 1.2.
You need to use
double c = Math.Ceiling(a / b) * b;

C#: divide an int by 100

How do I divide an int by 100?
eg:
int x = 32894;
int y = 32894 / 100;
Why does this result in y being 328 and not 328.94?
When one integer is divided by another, the arithmetic is performed as integer arithmetic.
If you want it to be performed as float, double or decimal arithmetic, you need to cast one of the values appropriately. For example:
decimal y = ((decimal) x) / 100;
Note that I've changed the type of y as well - it doesn't make sense to perform decimal arithmetic but then store the result in an int. The int can't possibly store 328.94.
You only need to force one of the values to the right type, as then the other will be promoted to the same type - there's no operator defined for dividing a decimal by an integer, for example. If you're performing arithmetic using several values, you might want to force all of them to the desired type just for clarity - it would be unfortunate for one operation to be performed using integer arithmetic, and another using double arithmetic, when you'd expected both to be in double.
If you're using literals, you can just use a suffix to indicate the type instead:
decimal a = x / 100m; // Use decimal arithmetic due to the "m"
double b = x / 100.0; // Use double arithmetic due to the ".0"
double c = x / 100d; // Use double arithmetic due to the "d"
double d = x / 100f; // Use float arithmetic due to the "f"
As for whether you should be using decimal, double or float, that depends on what you're trying to do. Read my articles on decimal floating point and binary floating point. Usually double is appropriate if you're dealing with "natural" quantities such as height and weight, where any value will really be an approximation; decimal is appropriate with artificial quantities such as money, which are typically represented exactly as decimal values to start with.
328.94 is not an integer. Integer / divide rounds down; that is how it works.
I suggest you cast to decimal:
decimal y = 32894M / 100;
or with variables:
decimal y = (decimal)x / 100;
Because an int is only a whole number. Try this instead.
int x = 32894;
double y = x / 100.0;
Because you're doing integer division. Add a period behind the 100 and you'll get a double instead.
When you divide two integers, the result is an integer. Integers don't have decimal places, so they're just truncated.
its programming fundamental that int(integer) dividing is different from float(floating point) dividing.
if u want .94 use float or double
var num = 3294F/100F

Categories