1

I have been trying one of the Project Euler challenges but I have gotten stuck with an annoying problem.

double sum = 0;
string numbers = "3710728753390210279...."

foreach (int item in numbers) sum += item;

Console.WriteLine(sum);
Console.ReadLine();

When I run this code it doesn't split each number how I expect it to e.g. the first number 3 will instead be 51 and the second number 7 will be 55. I don't understand where it gets these numbers from.

Thanks in advance.

Charlie
  • 13
  • 3
  • The ASCII value of the character 3 is 51 – Ghost Feb 20 '19 at 23:14
  • `foreach` treats its collection as `IEnumerable`, and you are passing a `String`, which implements `IEnumerable`. In C#, `char` has an implicit conversion to `int`, namely the value of the character in the code set (ASCII/Unicode). – NetMage Feb 20 '19 at 23:54
  • Why do you have `sum` as a `double`? – Enigmativity Feb 20 '19 at 23:56
  • If this is regarding Project Euler Problem 13 (https://projecteuler.net/problem=13), your approach does not lead to the correct solution. You have an array (or a string) of 100 numbers, each with 50 digits, and are asked to provide the first 10 digits of the sum of all these 50-digit numbers. In C# this is relatively trivial using System.Math.BigInteger and LINQ. – dumetrulo Feb 21 '19 at 13:32

4 Answers4

4

The other answers here haven't explained why you are seeing those unexpected numbers.

I think you are probably expecting the loop foreach (int item in numbers) to loop through the individual "numbers" in the string and automatically cast these numbers to integers. That's not what's happening (well, it is, but not how you expect).

The foreach loop is converting the string to IEnumerable<char> and iterating through each char character in the string starting '3', '7', '1', ....

In .Net characters and strings are encoded in unicode UTF-16 (as @TomBlodget pointed out in the comments). This means that each char can be converted to it's character code unit. Your code will actually sum the character code units.

In C# the code units for the characters '0', '1',..,'9' is in the range 48,...,57. For this reason you can do something like @Yeldar's answer:

foreach (char item in numbers) 
    sum += item - '0';     //  if item == '9' this is equivalent to 57 - 48 = 9

So, if the string only contains numbers then subtracting the '0' character will implicitly convert the char to it's int counterpart and you will end up with the actual numerical value it represents (ie '7' - '0' => 55 - 48 = 7).

The other answers here provide solutions to overcome the issue. I thought it would be useful explain why it was happening.

haldo
  • 14,512
  • 5
  • 46
  • 52
  • .NET text datatypes use the UTF-16 character encoding of the [Unicode](http://www.unicode.org/charts/nameslist/index.html) character set; not ASCII. (Just like JavaScript, Java, VB4/5/6/A/Script, …) – Tom Blodget Feb 21 '19 at 02:25
  • @TomBlodget that's true. But the designers of C# chose to implicitly convert `char` to `int` using the ASCII value for some reason. My answer has nothing to do with the character set encoding used in C# – haldo Feb 21 '19 at 09:04
  • 1
    @Haldo - No, it's not right to say "the designers of C# chose to implicitly convert `char` to `int` using the ASCII value". It is simply the case that a `char` has the ASCII value. What they did do, which is different, was overload mathematical operators on `char` to return an `int`. – Enigmativity Feb 21 '19 at 09:08
  • Please don't use to the term "ASCII value" when not referring to the ASCII character set or encoding. A more generic term is character code. Or with different specific contexts, codepoint (for a member of a character set) or code unit (for the result of applying a character encoding to a codepoint). In the case of .NET text data types, the values are UTF-16 code units. – Tom Blodget Feb 21 '19 at 12:18
  • @TomBlodget thank you for your comments. Would the wording be (more) correct if I changed it to "Your code will actually sum the character codes (using ASCII character set)" and "The character codes for the characters `'0', '1',...'9'` (using ASCII character set) is...". I appreciate any feedback. – haldo Feb 21 '19 at 12:45
  • Why is the existence of a character set and encoding called ASCII relevant to a system that doesn't use them? (Can you identify a system that does and cite its specification? One answer is [RFC 7230](https://tools.ietf.org/html/rfc7230) But would it be relevant to C#?) – Tom Blodget Feb 21 '19 at 21:51
  • @TomBlodget I understand what you mean... it's not relevant. actually embarrassed by my response to your first comment now! – haldo Feb 22 '19 at 00:32
  • Learning is hard; Un-learning is harder. – Tom Blodget Feb 22 '19 at 02:27
1

If you know that the string only contains numerals then this works:

string numbers = "3710728753390210279";
int sum = numbers.Sum(x => x - '0');

If you're not sure it contains only numerals then this will filter out non-numerals:

int sum = numbers.Where(char.IsDigit).Sum(x => x - '0');
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
0

If you are sure that there are only digits in a string, then you can actually subtract the value of char '0', which will do the magic for you:

int sum = 0;
string numbers = "371";

foreach (char item in numbers) 
    sum += item - '0';

Console.WriteLine(sum);

Please, note that there is char in foreach so that there is no implicit cast to int. And also int is used for sum, instead of double, because you don't actually need a floating-point number here.

Yeldar Kurmangaliyev
  • 33,467
  • 12
  • 59
  • 101
  • This is a succinct solution, note that it has no fault tolerance, and will produce unexpected results if the string contains anything but numbers – TheGeneral Feb 20 '19 at 23:38
0

This solution is a little more verbose but it will let you see clearly what is happening and will also exclude any non-numeric characters that may pop up in the string:

double sum = 0;
string numbers = "3710728753390210279";

foreach (char item in numbers)
{
    int intVal;
    if(int.TryParse(item.ToString(), out intVal))
        sum += intVal;
}
Console.WriteLine(sum);
Andrew
  • 59
  • 5