0

I made this function. It seemed it's working but when it comes to 20 digits number, the return value was 19. I'm wondering why this problem happen..

My function

function sumDigits($n) {
    return strlen($n);
}

echo sumDigits(100);  //3
echo sumDigits(1000);  //4
echo sumDigits(12345);  //5
echo sumDigits(1000000000);  //10
echo sumDigits(145874589632);  //12
echo sumDigits(0);  //1
echo sumDigits(12345698745254856320);  //19  <-- Why not 20?

Can you please somebody explain for me?

Thank you so much.

Suzuran
  • 39
  • 1
  • 7

3 Answers3

3

First, I would point out that the name of your function is misleading, as you are not really summing the values of the digits, but are counting the digits. So I would call your function countDigits instead of sumDigits.

The reason why it doesn't work for large numbers, is that the string representation will switch to scientific notation, so you're actually getting the length of "1.2345698745255E+19" not of "12345698745254856320"

If you are only interested in integers, you will get better results with the logarithm:

function countDigits($n) {
    return ceil(log10($n));
}

For numbers that have decimals, there is no good solution, since the precision of 64-bit floating pointing point numbers is limited to about 16 significant digits, so even if you provide more digits, the trailing decimals will be dropped -- this has nothing to do with your function, but with the precision of the number itself. For instance, you'll find that these two literals are equal:

if (1.123456789123456789123456789 == 1.12345678912345678) echo "equal";
trincot
  • 317,000
  • 35
  • 244
  • 286
  • 1
    Thank you for your advice about naming and detailed explanation. I didn't know that the string representation switches to scientific notation, I'm glad to know that. I appreciate you! – Suzuran May 14 '21 at 18:10
1

Because you function parameter is an integer, exceeding the limit. If you dump it, it actually shows the following: 1.2345698745255E+19 - which is 19 letters.

If you would do the following, it will return 20 - mind the quotes, which declares the input as string.

echo sumDigits("12345698745254856320"); //19 <-- Why not 20? -> now will be 20

Tyralcori
  • 1,079
  • 13
  • 33
1

As per documentation, strlen() expects a string so a cast happens. With default settings you get 1.2345698745255E+19:

var_dump((string)12345698745254856320);
string(19) "1.2345698745255E+19"

The root issue is that PHP converts your integer literal to float because it exceeds PHP_INT_MAX so it cannot be represented as integer:

var_dump(12345698745254856320, PHP_INT_MAX);

In 64-bit PHP:

float(1.2345698745254857E+19)
int(9223372036854775807)

You could change display settings to avoid E notation but you've already lost precision at this point.

Computer languages that store integers as a fixed amount of bytes do not allow arbitrary precision. Your best chance is to switch to strings:

var_dump('12345698745254856320', strlen('12345698745254856320'));
string(20) "12345698745254856320"
int(20)

... and optionally use an arbitrary precision library such as BCMath or GMP if you need actual maths.

It's also important to consider that this kind of issues is sometimes a symptom that your input data is not really meant to be an integer but just a very long digit-only string.

Álvaro González
  • 142,137
  • 41
  • 261
  • 360
  • 1
    Thank you for your helpful answer. I was just trying to solve a questions on Edabit and I got this issue. It was good I asked here as you guys' answers were informative and made me realise what I need to learn about. Thanks a lot! – Suzuran May 14 '21 at 18:25