C#: Random.NextDouble and including borders of the custom interval - c#

I've used that formula for gettting a random double in custom interval:
Random r = new Random();
double Upper = 3.7, Lower = 11.4, Result;
Result = Lower + (r.NextDouble() * (Upper - Lower))
// Lower is the lower border of interval, Upper is the upper border of interval
But keep in mind what MSDN says about NextDouble method:
A double-precision floating point number greater than or equal to 0.0, and less than 1.0.
That means interval in my sample code would include 3.7, but we can never get 11.4, right?
How can I include the upper border?
Lower + (r.NextDouble() * (Upper - Lower + double.Epsilon))
Can this formula help? Or there is another variant of getting random double numbers in [3.7 ; 11.4] (including both borders) ?

Do you really need the upper interval for the double case? The odds of hitting exactly that value are really, really small, and should be statistically insignificant for almost all scenarios. If you're interested in numbers with a certain number of decimal places, then you can use some rounding to achieve what you need.

Since your using doubles what kind of precision do you actually use? Rounding the numbers might be enough. Alternatively you can use your own scaling like this:
static void Main(string[] args)
{
var r = new Random(3);
for (int i = 0; i < 100; i++)
{
Console.WriteLine(r.NextDouble(0, 1, 100));
}
Console.ReadKey();
}
public static double NextDouble(this Random r
, double lower
, double upper
, int scale = int.MaxValue - 1
)
{
var d = lower + ((r.Next(scale + 1)) * (upper - lower) / scale);
return d;
}
That will give you the lower and upper inclusive range at the specified scale. I threw in a default value for scale which gives you the highest possible precision, using this method.

The precision itself is a problem here, since 3.7 neither 11.4 have a precise double representation.
I think that since you are using random double precision number, I don't think this imprecision is something to care about.

Add the following
Result = Math.Round(Result, 8)
and voila.
The number 8 is the decimal places it will round to. When the random number is within 8 decimal places of the upper bound (example: 11.3999999990) then the result will round to the bound (answer: 11.4000000000).
Of course the round occurs for all the numbers, so choose your precision carefully. It really depends on the application if 8 decimal places is good. Your limits are 1 to 15.

Related

Code for calculating amount of currency thinks it’s quirky. (Unintended Decimals keep appearing)

I’m just starting out learning C# through CodeAcademy. I’m supposed to write a program to calculate the least amount of “coins” of different values that are needed to reach a specified amount. It all went fine while following the instructions, but at the end of the exercise you are encouraged to write some more code to make the program work with decimal inputs (instead of just whole numbers).
I essentially copied the same code used for the complete amounts with slight modifications (multiplying the initial amount by 100) so that it could still run and give the required results. However, the last value (bronze cents) keeps giving me numbers with decimals for some reason. I thought about using Math.Floor() but after a few trials I realized it isn’t always an excess. Can anyone provide some help? Are there any inherent limits to the Math.Floor() command that I should have been aware of? Did I just do a big dumb?
tl;dr: noob coder here, wondering why code not do what i want it to do
using System;
namespace MoneyMaker
{
class MainClass
{
public static void Main(string[] args)
{
// This code is meant to divide an (user given) amount of money into coins of different values.
// First we ask for an input.
Console.WriteLine("Welcome to Money Maker!.00");
Console.WriteLine("Please enter the amount you want to divide in Coins:");
string strAmount = Console.ReadLine();
Console.WriteLine($"${strAmount} Canopy is equal to:");
double wholeAmount = Convert.ToDouble(strAmount);
// These are the values of each coin.
// The cents are multiplied by 100 for the purposes of using then in the code.
// The bronze coins are listed, but unused, since their value is essentially 1.
double gold = 10;
double silver = 5;
//double bronze = 1;
double smolGold = .1 * 100;
double smolSilver = .05 * 100;
//double smolBronze = .01 * 100;
// These lines calculate the integral values (gold, silver and bronze coins).
double douAmount = Math.Floor(wholeAmount);
double goldCoins = Math.Floor(douAmount / gold);
double silAmount = douAmount % gold;
double silverCoins = Math.Floor(silAmount / silver);
double bronzeCoins = silAmount % silver;
// These lines calculate the decimal values (gold, silver and bronze cents).
// They start by multiplying the cents by 100, rendering the rest of the lines the same as in the integral values.
double smolAmount = 100 * (wholeAmount - douAmount);
double goldCents = Math.Floor(smolAmount / smolGold);
double littleSilver = smolAmount % smolGold;
double silverCents = Math.Floor(littleSilver / smolSilver);
//ERROR: There seems to be an issue with the bronze cents, returning a value with decimals.
double bronzeCents = littleSilver % smolSilver;
// Finally, the output string with the results:
Console.WriteLine($"\n Gold Coins: {goldCoins} \n Silver Coins: {silverCoins} \n Bronze Coins: {bronzeCoins} \n Gold Cents: {goldCents} \n Silver Cents: {silverCents} \n Bronze Cents: {bronzeCents}");
}
}
}
Never use doubles for currency. Use decimal.
Use double when representing a physical quantity, like length, mass, speed, and so on, where insignificant representation errors do not matter. Use decimal to represent money, where every penny or fraction of a penny matters.
The difference is: double represents numbers as a fraction where the denominator is a power of two, like "5/32nds". decimal represents numbers as a fraction where the denominator is a power of ten, like "3/100ths". The former will tend to accumulate representation errors for fractions like "3/100ths", but the latter will not.
If your tutorial is suggesting that you use double arithmetic for computations involving money, get a better tutorial.
All that said, I suspect you may misunderstand what the % operator is for if you are using it on non-integer quantities. It is primarily for use on integer quantities, so if you're using it on decimals or doubles, something strange is probably going on.

How to distribute a decimal by .5 as evenly as possible with no rounding

I have an input number that is guaranteed to be specified as n.0 or n.5
Example inputs (only a single input is provided):
0
0.5
13.0
13.5
13337.5
28091.0
I need to distribute this number by n values so that all values total the input exactly, but all values must be n.0 or n.5 with NO rounding allowed.
For example, if 13.5 is input and needs to be distributed between 4, the resulting values would be:
3.5
3.5
3.5
3.0
Another example, if 1.0 is specified and needs to be distributed between 4, the resulting values would be:
0.5
0.5
0.0
0.0
I'm thinking the function signature would look similar to:
private List<decimal> DistributeEvenly(decimal amount, int numDistributions)
{
...
}
I do find examples of how to distribute numbers evenly, but not with the n.0 and n.5 requirement and also not without rounding. How can this be accomplished?
I would just divide the input amount by the number of distributions, ignoring any remainder and use that as the base value. Then take the remainder and subtract 0.5 for each distribution until it is gone. Something like this:
private List<decimal> DistributeEvenly(decimal amount, int numDistributions)
{
var result = new List<decimal>();
//Determines the whole number part of each distribution
decimal baseValue = (int)(amount / numDistributions);
//Fill the list with the baseValue, numDistributions times
result.AddRange(Enumerable.Repeat(baseValue, numDistributions));
//The remainder will be distributed in amounts of 0.5 until it runs out
decimal remainder = amount - (baseValue * numDistributions);
int index = 0;
while (remainder > 0)
{
result[index] += 0.5m;
index++;
if (index >= numDistributions)
{
index = 0;
}
remainder -= 0.5m;
}
return result;
}
If you find the total of the numbers you're given and divide by the number of outputs required, you get the mean that's needed. If you divide this mean value by one-half (equivalently: multiply it by 2), take the floor of the result, and then multiply the result of that by one-half, you get the smaller of the two distinct numbers that need to be in your output list. It is simple from there to work out how many of the output numbers need to be increased by one-half.
Just multiply every number by two, and then distribute them evenly by increments of 1 in a for loop until your total is 0. Divide the result by 2. (Or just do it with floats, but you might get rounding errors)

Double every time brings different values

It's my generating algorithm it's generating random double elements for the array which sum must be 1
public static double [] GenerateWithSumOfElementsIsOne(int elements)
{
double sum = 1;
double [] arr = new double [elements];
for (int i = 0; i < elements - 1; i++)
{
arr[i] = RandomHelper.GetRandomNumber(0, sum);
sum -= arr[i];
}
arr[elements - 1] = sum;
return arr;
}
And the method helper
public static double GetRandomNumber(double minimum, double maximum)
{
Random random = new Random();
return random.NextDouble() * (maximum - minimum) + minimum;
}
My test cases are:
[Test]
[TestCase(7)]
[TestCase(5)]
[TestCase(4)]
[TestCase(8)]
[TestCase(10)]
[TestCase(50)]
public void GenerateWithSumOfElementsIsOne(int num)
{
Assert.AreEqual(1, RandomArray.GenerateWithSumOfElementsIsOne(num).Sum());
}
And the thing is - when I'm testing it returns every time different value like this cases :
Expected: 1
But was: 0.99999999999999967d
Expected: 1
But was: 0.99999999999999989d
But in the next test, it passes sometimes all of them, sometimes not.
I know that troubles with rounding and ask for some help, dear experts :)
https://en.wikipedia.org/wiki/Floating-point_arithmetic
In computing, floating-point arithmetic is arithmetic using formulaic
representation of real numbers as an approximation so as to support a
trade-off between range and precision. For this reason, floating-point
computation is often found in systems which include very small and
very large real numbers, which require fast processing times. A number
is, in general, represented approximately to a fixed number of
significant digits (the significand) and scaled using an exponent in
some fixed base; the base for the scaling is normally two, ten, or
sixteen.
In short, this is what floats do, they dont hold every single value and do approximate. If you would like more precision try using a Decimal instead, or adding tolerance by an epsilon (an upper bound on the relative error due to rounding in floating point arithmetic)
var ratio = a / b;
var diff = Math.Abs(ratio - 1);
return diff <= epsilon;
Round up errors are frequent in case of floating point types (like Single and Double), e.g. let's compute an easy sum:
// 0.1 + 0.1 + ... + 0.1 = ? (100 times). Is it 0.1 * 100 == 10? No!
Console.WriteLine((Enumerable.Range(1, 100).Sum(i => 0.1)).ToString("R"));
Outcome:
9.99999999999998
That's why when comparing floatinfg point values with == or != add tolerance:
// We have at least 8 correct digits
// i.e. the asbolute value of the (round up) error is less than tolerance
Assert.IsTrue(Math.Abs(RandomArray.GenerateWithSumOfElementsIsOne(num).Sum() - 1.0) < 1e-8);

Imitate Math.Round for GnuMP library in C#

I downloaded for myself GnuMP library: https://gnumpnet.codeplex.com/ which behind the curtains uses gmp.dll which is wrapper for https://gmplib.org.
It has type Real, which is used for high precision calculations. In other project I have double and decimal type, which I want to replace with Real.
I need to replace Math.Round() with customized round for Real ( type in gnump.net). Did anybody tried to implement Round but for Real of Gnump.
Today I found an mathematically correct answer:
public static Real Round(Real r, int precision)
{
Real scaled = Real.Pow(10, precision + 1);
Real multiplied = r*scaled;
Real truncated = Trunc(multiplied);
Real lastNumber = truncated - Trunc(truncated/10)*10;
if (lastNumber >= 5)
{
truncated += 10;
}
truncated = Trunc(truncated/10);
return truncated * 10 /(scaled);
}
When I say mathematically correct I mean that following code:
Real r = 2.5;
r = Real.Round(r, 0);
will give 3. According to http://msdn.microsoft.com/en-us/library/wyk4d9cy.aspx Math.Round will give "round to even" (so called banker's rounding), but I for my task need mathematical round.

c# float [] average loses accuracy

I am trying to calculate average for an array of floats. I need to use indices because this is inside a binary search so the top and bottom will move. (Big picture we are trying to optimize a half range estimation so we don't have to re-create the array each pass).
Anyway I wrote a custom average loop and I'm getting 2 places less accuracy than the c# Average() method
float test = input.Average();
int count = (top - bottom) + 1;//number of elements in this iteration
int pos = bottom;
float average = 0f;//working average
while (pos <= top)
{
average += input[pos];
pos++;
}
average = average / count;
example:
0.0371166766 - c#
0.03711666 - my loop
125090.148 - c#
125090.281 - my loop
http://pastebin.com/qRE3VrCt
I'm getting 2 places less accuracy than the c# Average()
No, you are only losing 1 significant digit. The float type can only store 7 significant digits, the rest are just random noise. Inevitably in a calculation like this, you can accumulate round-off error and thus lose precision. Getting the round-off errors to balance out requires luck.
The only way to avoid it is to use a floating point type that has more precision to accumulate the result. Not an issue, you have double available. Which is why the Linq Average method looks like this:
public static float Average(this IEnumerable<float> source) {
if (source == null) throw Error.ArgumentNull("source");
double sum = 0; // <=== NOTE: double
long count = 0;
checked {
foreach (float v in source) {
sum += v;
count++;
}
}
if (count > 0) return (float)(sum / count);
throw Error.NoElements();
}
Use double to reproduce the Linq result with a comparable number of significant digits in the result.
I'd rewrite this as:
int count = (top - bottom) + 1;//number of elements in this iteration
double sum = 0;
for(int i = bottom; i <= top; i++)
{
sum += input[i];
}
float average = (float)(sum/count);
That way you're using a high precision accumulator, which helps reduce rounding errors.
btw. if performance isn't that important, you can still use LINQ to calculate the average of an array slice:
input.Skip(bottom).Take(top - bottom + 1).Average()
I'm not entirely sure if that fits your problem, but if you need to calculate the average of many subarrays, it can be useful to create a persistent sum array, so calculating an average simply becomes two table lookups and a division.
Just to add to the conversation, be careful when using Floating point primitives.
What Every Computer Scientist Should Know About Floating-Point Arithmetic
Internally floating point numbers store additional least significant bits that are not reflected in the displayed value (aka: Guard Bits or Guard Digits). They are, however, utilized when performing mathematical operations and equality checks. One common result is that a variable containing 0f is not always zero. When accumulating floating point values this can also lead to precision errors.
Use Decimal for your accumulator:
Will not have rounding errors due to Guard Digits
Is a 128bit data type (less likely to exceed Max Value in your accumulator).
For more info:
What is the difference between Decimal, Float and Double in C#?

Categories

Resources