我最近在班上进行了一次测试。其中一个问题如下:
给定一个数字n,用C/ c++编写一个函数,返回该数字的数字和的平方。(以下是重要的)。n的取值范围是[-(10^7),10^7]。示例:如果n = 123,函数应该返回14(1^2 + 2^2 + 3^2 = 14)。
这是我写的函数:
int sum_of_digits_squared(int n)
{
int s = 0, c;
while (n) {
c = n % 10;
s += (c * c);
n /= 10;
}
return s;
}
在我看来是这样的。所以现在测试回来了,我发现老师没有给我所有的分数,原因是我不明白。根据他的说法,为了使我的功能完整,我应该添加以下细节:
int sum_of_digits_squared(int n)
{
int s = 0, c;
if (n == 0) { //
return 0; //
} //
// THIS APPARENTLY SHOULD'VE
if (n < 0) { // BEEN IN THE FUNCTION FOR IT
n = n * (-1); // TO BE CORRECT
} //
while (n) {
c = n % 10;
s += (c * c);
n /= 10;
}
return s;
}
它的论点是数字n在[-(10^7),10^7]的范围内,所以它可以是一个负数。但是我不知道我自己版本的函数哪里失败了。如果我理解正确,while(n)的含义是while(n != 0),而不是while(n > 0),所以在我的函数版本中,数字n不会失败进入循环。这还是一样的。
Then, I tried both versions of the function on my computer at home and I got exactly the same answers for all the examples that I tried. So, sum_of_digits_squared(-123) is equal to sum_of_digits_squared(123) (which again, is equal to 14) (even without the detail that I apparently should've added). Indeed, if I try to print on the screen the digits of the number (from least to greatest in importance), in the 123 case I get 3 2 1 and in the -123 case I get -3 -2 -1 (which is actually kind of interesting). But in this problem it wouldn't matter since we square the digits.
那么,谁错了呢?
编辑:我的错,我忘了说明,也不知道这很重要。我们的类和测试中使用的C版本必须是C99或更新版本。所以我猜(通过阅读评论)我的版本无论如何都能得到正确答案。
注意:当我在写这个答案时,你确实澄清了你使用的是C。我的答案大部分是关于c++的。然而,由于你的标题仍然是c++,问题的标签仍然是c++,我还是选择了回答,以防这对其他人仍然有用,特别是因为到目前为止我看到的大多数答案都是不令人满意的。
在现代c++中(注:我真的不知道C在这方面的立场),你的教授似乎在这两个方面都是错误的。
首先是这部分:
if (n == 0) {
return 0;
}
在c++中,这基本上等同于:
if (!n) {
return 0;
}
这意味着你的while相当于这样的东西:
while(n != 0) {
// some implementation
}
这意味着,既然你只是在while不会执行的情况下退出if语句,就没有理由把这个if语句放在这里,因为你在循环之后所做的事情和在if语句中所做的事情是等价的。尽管我应该说因为某些原因它们是不同的,你需要这个if。
实际上,这个if语句并不是特别有用,除非我弄错了。
第二部分是事情变得棘手的地方:
if (n < 0) {
n = n * (-1);
}
问题的核心是输出负数的模量输出什么。
在现代c++中,这似乎被很好地定义了:
二进制/运算符得到商,二进制%运算符得到第一个表达式除以第二个表达式的余数。如果/或%的第二个操作数为零,则行为未定义。对于整型操作数,/运算符产生除任何小数部分的代数商;如果商a/b在结果类型中是可表示的,(a/b)*b + a%b等于a。
后来:
如果两个操作数都是非负的,那么余数也是非负的;如果不是,其余部分的符号是实现定义的。
正如引用答案的海报正确指出的那样,这个等式的重要部分就在这里:
(a/b)*b + a%b
以你的案例为例,你会得到这样的东西:
-13/ 10 = -1 (integer truncation)
-1 * 10 = -10
-13 - (-10) = -13 + 10 = -3
唯一的陷阱是最后一句:
如果两个操作数都是非负的,那么余数也是非负的;如果不是,其余部分的符号是实现定义的。
这意味着在这种情况下,似乎只有符号是实现定义的。在你的例子中,这应该不是问题,因为,因为你对这个值平方。
也就是说,请记住,这并不一定适用于早期版本的c++或C99。如果你的教授用的就是这个,那可能就是原因所在。
编辑:不,我错了。这似乎是C99或更高版本的情况:
C99要求当a/b是可表示的时:
(a/b) * b + a%b等于a
另一个地方:
When integers are divided and the division is inexact, if both operands are positive the result of the / operator is the largest integer less than the algebraic quotient and the result of the % operator is positive. If either operand is negative, whether the result of the / operator is the largest integer less than the algebraic quotient or the smallest integer greater than the algebraic quotient is implementation-defined, as is the sign of the result of the % operator. If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a.
Does either ANSI C or ISO C specify what -5 % 10 should be?
所以,是的。即使在C99中,这似乎也不会影响到你。方程是一样的。
总结一下评论中流传的讨论:
There is no good reason to test in advance for n == 0. The while(n) test will handle that case perfectly.
It's likely your teacher is still used to earlier times, when the result of % with negative operands was differently defined. On some old systems (including, notably, early Unix on a PDP-11, where Dennis Ritchie originally developed C), the result of a % b was always in the range [0 .. b-1], meaning that -123 % 10 was 7. On such a system, the test in advance for n < 0 would be necessary.
但第二颗子弹只适用于更早的时代。在C和c++标准的当前版本中,整数除法被定义为向0截断,因此即使n为负数,n % 10也保证会给出n的最后一位(可能是负数)。
因此,对于“while(n)的含义是什么?”这个问题的答案是“与while(n != 0)完全相同”,而对于“这段代码对于负n和正n是否都能正常工作?”的答案是“在任何现代的、符合标准的编译器下都能正常工作”。“那为什么老师要把它记下来?”这个问题的答案很可能是他们没有意识到1999年C语言和2010年左右c++发生了重大的语言重新定义。
注意:当我在写这个答案时,你确实澄清了你使用的是C。我的答案大部分是关于c++的。然而,由于你的标题仍然是c++,问题的标签仍然是c++,我还是选择了回答,以防这对其他人仍然有用,特别是因为到目前为止我看到的大多数答案都是不令人满意的。
在现代c++中(注:我真的不知道C在这方面的立场),你的教授似乎在这两个方面都是错误的。
首先是这部分:
if (n == 0) {
return 0;
}
在c++中,这基本上等同于:
if (!n) {
return 0;
}
这意味着你的while相当于这样的东西:
while(n != 0) {
// some implementation
}
这意味着,既然你只是在while不会执行的情况下退出if语句,就没有理由把这个if语句放在这里,因为你在循环之后所做的事情和在if语句中所做的事情是等价的。尽管我应该说因为某些原因它们是不同的,你需要这个if。
实际上,这个if语句并不是特别有用,除非我弄错了。
第二部分是事情变得棘手的地方:
if (n < 0) {
n = n * (-1);
}
问题的核心是输出负数的模量输出什么。
在现代c++中,这似乎被很好地定义了:
二进制/运算符得到商,二进制%运算符得到第一个表达式除以第二个表达式的余数。如果/或%的第二个操作数为零,则行为未定义。对于整型操作数,/运算符产生除任何小数部分的代数商;如果商a/b在结果类型中是可表示的,(a/b)*b + a%b等于a。
后来:
如果两个操作数都是非负的,那么余数也是非负的;如果不是,其余部分的符号是实现定义的。
正如引用答案的海报正确指出的那样,这个等式的重要部分就在这里:
(a/b)*b + a%b
以你的案例为例,你会得到这样的东西:
-13/ 10 = -1 (integer truncation)
-1 * 10 = -10
-13 - (-10) = -13 + 10 = -3
唯一的陷阱是最后一句:
如果两个操作数都是非负的,那么余数也是非负的;如果不是,其余部分的符号是实现定义的。
这意味着在这种情况下,似乎只有符号是实现定义的。在你的例子中,这应该不是问题,因为,因为你对这个值平方。
也就是说,请记住,这并不一定适用于早期版本的c++或C99。如果你的教授用的就是这个,那可能就是原因所在。
编辑:不,我错了。这似乎是C99或更高版本的情况:
C99要求当a/b是可表示的时:
(a/b) * b + a%b等于a
另一个地方:
When integers are divided and the division is inexact, if both operands are positive the result of the / operator is the largest integer less than the algebraic quotient and the result of the % operator is positive. If either operand is negative, whether the result of the / operator is the largest integer less than the algebraic quotient or the smallest integer greater than the algebraic quotient is implementation-defined, as is the sign of the result of the % operator. If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a.
Does either ANSI C or ISO C specify what -5 % 10 should be?
所以,是的。即使在C99中,这似乎也不会影响到你。方程是一样的。