This is one of those cases where I assume the solution is on google, but I have no idea what the search term even is. Or even which Tags to use on Stack Overflow.
Situation:
Floats are not precise. A common trick around it, is to use Integers for the math, then shift the decimal point during output:
If you need 4 decimal places of precision for € (not uncommon in finances), you effectively measure and calculate in milli-Euro. Then during output you shift the decimal left, and cut off the last 2 digits:
So a int 1234567 that is stored, retrieved and processes as a int, would print as/stand for "123.45 €" to the normal end user.
Problem:
But how would this interact with localisation? .NET has this awesome part where Parse() and ToString() look at the current Windows Users Culture setting, to figure out what the decimal separator, group separator and group size is today.
There seems to be no fixed point type in .NET, to do the work for me.
Is there some format string combination I could use to say "shift decimal point 4 left into the number, omit last 2 digits)"? F2 would only add two empty 0 past the decimal point for any int. and "1234567.00 €" is a few orders of magnitude off from "123.45 €"
My best idea thus far:
Building my own format string out of the NumberDecimalSeparator, NumberGroupSeparator and NumberGroupSizes from the users culture - but that is just ugly and seems like something I should not be doing. Is there a better day to do it, or is it one of those cases where I have to go for the ugly thing that works?
Maybe something like this
using System.Globalization;
int n = 123456;
string price = "";
string priceW = "";
int i = 0;
foreach(char c in n.ToString())
{
i++;
if(i == n.ToString().Length - 1)
price += ".";
price += c;
}
float rPrice = float.Parse(price, CultureInfo.InvariantCulture.NumberFormat);
priceW = price + "€";
Related
So I'm a complete newb to unity and c# and I'm trying to make my first mobile incremental game. I know how to format a variable from (e.g.) 1000 >>> 1k however I have several variables that can go up to decillion+ so I imagine having to check every variable's value seperately up to decillion+ will be quite inefficient. Being a newb I'm not sure how to go about it, maybe a for loop or something?
EDIT: I'm checking if x is greater than a certain value. For example if it's greater than 1,000, display 1k. If it's greater than 1,000,000, display 1m...etc etc
This is my current code for checking if x is greater than 1000 however I don't think copy pasting this against other values would be very efficient;
if (totalCash > 1000)
{
totalCashk = totalCash / 1000;
totalCashTxt.text = "$" + totalCashk.ToString("F1") + "k";
}
So, I agree that copying code is not efficient. That's why people invented functions!
How about simply wrapping your formatting into function, eg. named prettyCurrency?
So you can simply write:
totalCashTxt.text = prettyCurrency(totalCashk);
Also, instead of writing ton of ifs you can handle this case with logarithm with base of 10 to determine number of digits. Example in pure C# below:
using System.IO;
using System;
class Program
{
// Very simple example, gonna throw exception for numbers bigger than 10^12
static readonly string[] suffixes = {"", "k", "M", "G"};
static string prettyCurrency(long cash, string prefix="$")
{
int k;
if(cash == 0)
k = 0; // log10 of 0 is not valid
else
k = (int)(Math.Log10(cash) / 3); // get number of digits and divide by 3
var dividor = Math.Pow(10,k*3); // actual number we print
var text = prefix + (cash/dividor).ToString("F1") + suffixes[k];
return text;
}
static void Main()
{
Console.WriteLine(prettyCurrency(0));
Console.WriteLine(prettyCurrency(333));
Console.WriteLine(prettyCurrency(3145));
Console.WriteLine(prettyCurrency(314512455));
Console.WriteLine(prettyCurrency(31451242545));
}
}
OUTPUT:
$0.0
$333.0
$3.1k
$314.5M
$31.5G
Also, you might think about introducing a new type, which implements this function as its ToString() overload.
EDIT:
I forgot about 0 in input, now it is fixed. And indeed, as #Draco18s said in his comment nor int nor long will handle really big numbers, so you can either use external library like BigInteger or switch to double which will lose his precision when numbers becomes bigger and bigger. (e.g. 1000000000000000.0 + 1 might be equal to 1000000000000000.0). If you choose the latter you should change my function to handle numbers in range (0.0,1.0), for which log10 is negative.
In c#, I want to force 0s when converting from double to string in case a number is lower than 100, the only challenge is that I want to keep all decimal places. Examples
58.3434454545 = 058.3434454545
8.343 = 008.343
I tried with ToString + a format provider but I'm not certain what's the correct provider for keeping all decimal places
You can use formatter strings for .ToString(), documented here.
To do what you want you can use this as example, noting the maximum digits for double is 17:
double numberA = 58.3434454545;
numberA.ToString("000.##############"); //058.3434454545
double numberB = 8.343;
numberB.ToString("000.##############"); //008.343
This is a rather ass ugly solution but if you wanted the number of decimals to be dynamic you could try something like this:
private string FormatDouble(double dbl)
{
int count = BitConverter.GetBytes(decimal.GetBits((decimal)dbl)[3])[2];
var fmt = string.Concat("000.", new string('#', count));
return dbl.ToString(fmt);
}
Call it like this:
Console.WriteLine(FormatDouble(58.3434454545123123));
Console.WriteLine(FormatDouble(8.3431312323));
And your output would be this:
058.3434454545123
008.3431312323
I'm sure there is a much better way to do this and I'm not sure about performance, but hey it works and you don't have to guess the number of decimals you need, so that's a plus
For accounting program I need to divide a price by 3 so it can be shared over 3 months
For example 9€
3€ first month
3€ second month
3€ third month
Now this would be price/3
But what if the number is 10?
3,33€ first month
3,33€ second month
3,33€ last month
3,33€*3 =€9.99
One cent has gone missing.
How can I make it so the ouput would become 3,33€ , 3,33€ , 3,34€?
You need to ask the accountant what they would want here. That's an important thing to do in software development: ask the users.
Normally, for stability, you would subtract the amounts paid from a balance account, and put checks in to ensure that the balance falls to zero.
And don't ever use a floating point data type when building accounting software. Floating point precision will bite you. Use a currency type instead.
You could set the last by making up the difference, instead of via the same calculation as the rest. In pseudocode:
normalMonthPrice = RoundToTwoPlaces(totalPrice / months);
lastMonthPrice = totalPrice - (normalMonthPrice * (months - 1));
As Bathsheba said, ask your users first.
Here's a technique that I've used often in such scenarios. This method will ensure the most even distribution, with the upward bias toward the end. For example, if you call DivvyUp(101, 3), you'll get 33.66, 33.67, 33.67. Notice that the difference isn't just made up for at the end. Instead, each value is computed according to what's left, not what was started with.
public static double[] DivvyUp(double total, uint count)
{
var parts = new double[count];
for (var i = 0; i < count; ++i)
{
var part = Math.Truncate((100d * total) / (count - i)) / 100d;
parts[i] = part;
total -= part;
}
return parts;
}
Congratulations, you've found out why the computing world isn't as simple as "put math on it"-
The easiest solution would be to divide by 3, round to two decimal places, and use that value for the first two months, and original - 2 * perMonth for the rest.
I want to display a number which is of double datatype in C#, in grouped digits and with two decimal places only if it contains a decimal no.
e.g. If there is 2000.4567 and 2000.45, it must be displayed as 2,000.45 and if it is 2000 then it will displayed as 2,000 (grouped but without decimal).
I have tried this and it is working fine for digit grouping but it rounds off the decimal no. to an integer value either by floor or ceil:
DimensionLength.ToString("#,##0")
DimensionLength is of type double.
Try this Code
double s=123.345345;
string str=string.Empty;
str = s.ToString("#,0.##");
MessageBox.Show(str);
I think you are better off by creating your own custom condition
double _inputval=2000.4567
string _outputVal="";
if ((_inputval % 1) == 0)
{
_outputVal = _inputval.ToString("#,##");
}
else
{
_outputVal = _inputval.ToString("N2");
}
Hope it helps
Seems like this should be something straightforward, but I haven't been able to get it right. I've looked at http://idunno.org/archive/2004/14/01/122.aspx for reference.
Example:
I would like to print a table of double values, with each double output having 3 decimal precision, and take up 10 spaces (left aligned). Conceptually, I tried something like this, but it only works with precision OR padding, not both:
foreach(line in lines)
{
foreach (double val in line)
{
Console.Write("{0:0.000,-10}", val);
}
Console.WriteLine()
}
Update: I can use padleft/padright in very simple scenarios, if i have more complicated output it becomes not very concise. Is there something similar to sprintf?
Try
double d = 3.14;
Console.WriteLine("{0,10:0.000}", d);
P.S: have a look at this article as a primer on string formatting. Also, string.Format should allow you doing everything sprintf does - and actually more... what else are you trying to do?
useful
|{0,-10:0.00}| => |87,87 | - With "-" => padRight
|{0,10:0.000}| => | 87,878| - Without "-" => padLeft
|{3,-10:0.###}| - ### - prints numbers after decimal "," only if it is meaningful (not 0): 87,8000 =>87,8