-1

I have an integer which I want to convert to a string, but taking the last 3 numbers and showing only the first 2 of them as decimals,

something like this: 85893 => 85.89 or failing that: 469 => 0.46

I have thought about rounding it, formatting it with string, even regex, but I would like to know what is the most optimal way to do it, since it will be done in several iterations per second

[UPDATE] The best performing option I found was: @juharr

(num / 1000M).ToString("F2");

thanks a lot

RoxoL
  • 9
  • 4
  • 5
    You could convert to decimal and divide by 1000. Like `(num / 1000M).ToString("F2");` – juharr Jan 19 '21 at 21:58
  • 1
    What about rounding? If the number is 85895 should you get back 85.90 or 85.89? – Steve Jan 19 '21 at 22:03
  • @juharr It seems like a good option – RoxoL Jan 19 '21 at 22:07
  • @RoxoL juharr's answer rounds 469 to 0.47, that doesn't meet the criteria of your expected results. – Ryan Wilson Jan 19 '21 at 22:22
  • @RyanWilson Its okay, I don't need an exact result, my priority is performance, – RoxoL Jan 19 '21 at 22:24
  • @RoxoL Then you shouldn't put what you did as expected results. Precision matters in lots of jobs and applications out there, ie. think banking or accounting. – Ryan Wilson Jan 19 '21 at 22:25
  • 4
    `I don't need an exact result, my priority is performance,` That isn't what your question says. I mean, if you want performance, just use "1". It is super fast. – mjwills Jan 19 '21 at 22:27
  • @RyanWilson You are right, I did not express myself clearly, I am new to programming I will take it into account for the next – RoxoL Jan 19 '21 at 22:28
  • Still, it's good to understand what kind of errors you might have (so you have an answer when your boss asks: "hey RoxoL, why did we get 85.90 instead of 85.89?"). You should explicitly round the number. – Flydog57 Jan 19 '21 at 22:35
  • 1
    Don't fall into the trap of trying to prematurely optimize. OFfen things that _seem_ like they'll be "slow" don;t make a significant difference in the _overall_ performance. For example, a user won't notice a difference between a number that takes 1ms to display and one that takes 10ms. – D Stanley Jan 19 '21 at 22:42

2 Answers2

0

If you care about precision, you could just take your numbers and multiply by 0.001, then take a Substring of it so that it chops off the last digit, I did it this way because it looks like you don't want it rounding depending on the last digit in the thousandths place of the decimal,

EDIT: After doing more research I found that if using newer versions of .Net you could use a System.ReadOnlySpan<T> since this is a reference to the string it won't create a new copy, so instead of the commented out line using .Substring() it would be like:

int num = 469;
string number = (num * 0.001).ToString("0.000");
//Console.WriteLine(number.Substring(0, number.Length - 1));
Console.WriteLine(number.AsSpan(0, number.Length - 1));

reference - (String.Substring() seems to bottleneck this code):

If you don't care about precision:

int num = 469;
Console.WriteLine(Math.Round(num * 0.001, 2));
Ryan Wilson
  • 10,223
  • 2
  • 21
  • 40
  • Perhaps it works, but in any case I think that all those String ops are not very good for performances. – Steve Jan 19 '21 at 22:09
  • @Steve There are two String ops, one call to `.ToString()` which is inevitable, and the call to `.Substring()`. The comment with the most upvotes returns a rounded value which is not what the OP wants by his second example result. – Ryan Wilson Jan 19 '21 at 22:10
  • Do you _have_ to convert it back to a string? If you can just use the decimal result you can save one string op (the `ToString`), but it will be called implicitly when you _display_ the string at any point. – D Stanley Jan 19 '21 at 22:15
  • @DStanley It explicitly states in the OP's post that it needs to be converted to a string. "I have an integer which I want to convert to a string, but taking the last 3 numbers and showing only the first 2 of them as decimals," Unless I misunderstand the OP's intent, but that seems pretty clear. – Ryan Wilson Jan 19 '21 at 22:16
  • 1
    You'll also be likelier to be more happy with a `decimal` rather than a `double`. As noted in other comments, you need to decide on a rounding strategy. Look up `MidpointRounding` – Flydog57 Jan 19 '21 at 22:33
  • @DStanley How about my updated answer? Using the newer `ReadOnlySpan`? – Ryan Wilson Jan 21 '21 at 20:17
0

It would appear your are truncating towards zero rather than rounding to get to 1/100ths.

I might do something like this:

public static string FormatSpecial( this int n )
{
  return Math.Round( ((decimal) n) / 1000 , 2 , MidpointRounding.ToZero ).ToString();
}

If that is insufficiently efficient, perhaps something like this:

public static string FormatSpecial( this int n )
{
  int ddd = n         / 1000 ; // integer portion
  int ttt = n         % 1000 ; // fractional portion (1/1000ths)
  int hh  = thousands /   10 ; // truncate thousandths to 1/100ths

  string s = string.Format("{0}.{1:00}", ddd, hh );
}

Just 3 integer divisions, followed by conversion to string.

I would hope that the optimizer and the JIT would collapse the 1st 2 divisions into a single machine instruction: on most CPUs, the integer division op usually produces both a quotient and remainder as its result.

If that's still insufficiently efficient, then something like this to optimize the string formatting. Since we know that the max value of a 64-bit unsigned integer (ulong) is 18446744073709551615, the resulting formatted string will never exceed 25 characters in length. So...

public static string FormatSpecial( this int n )
{
  const  BUFL          = 25             ;
  const  DECIMAL_POINT = BUFL - 4       ;
  const  MIN           = BUFL - 5       ;
  char[] buf           = new char[BUFL] ;
  int    i             = BUFL - 1       ;

  do
  {
    if ( i == DECIMAL_POINT)
    {
      buf[i--] = '.';
    }

    char digit = '0' + ( n % 10 );
    buf[i--] = digit ;

    n /= 10 ;

  } while ( n != 0 || i > MIN );

  int len = BUFL - i - 1;
  return new String( buf , i , len );
}
Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135