C语言 求平方数时是否需要明确处理负数或零?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/58224638/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Do I need to explicitly handle negative numbers or zero when summing squared digits?
提问by user010517720
I recently had a test in my class. One of the problems was the following:
我最近在课堂上进行了一次测试。问题之一如下:
Given a number n, write a function in C/C++ that returns the sum of the digits of the number squared. (The following is important). The rangeof nis [ -(10^7), 10^7 ]. Example: If n= 123, your function should return 14 (1^2 + 2^2 + 3^2 = 14).
给定一个数字n,用 C/C++ 编写一个函数,该函数返回该数字的平方数之和。(以下很重要)。的范围的Ñ为[ - (10 ^ 7),10 ^ 7]。示例:如果n= 123,您的函数应返回 14 (1^2 + 2^2 + 3^2 = 14)。
This is the function that I wrote:
这是我写的函数:
int sum_of_digits_squared(int n)
{
int s = 0, c;
while (n) {
c = n % 10;
s += (c * c);
n /= 10;
}
return s;
}
Looked right to me. So now the test came back and I found that the teacher didn't give me all the points for a reason that I do not understand. According to him, for my function to be complete, I should've have added the following detail:
对我来说是正确的。所以现在考试回来了,我发现老师因为我不明白的原因没有给我所有的分数。据他说,为了我的功能完整,我应该添加以下细节:
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;
}
The argument for this is that the number nis in the range [-(10^7), 10^7], so it can be a negative number. But I don't see where my own version of the function fails. If I understand correctly, the meaning of while(n)is while(n != 0), notwhile (n > 0), so in my version of the function the number nwouldn't fail to enter the loop. It would work just the same.
对此的论点是数字n在 [-(10^7), 10^7] 范围内,因此它可以是负数。但我没有看到我自己的函数版本在哪里失败。如果我理解正确,它的意思是while(n)is while(n != 0),notwhile (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 123case I get 3 2 1and in the -123case I get -3 -2 -1(which is actually kind of interesting). But in this problem it wouldn't matter since we square the digits.
然后,我在家里的计算机上尝试了该函数的两个版本,对于我尝试过的所有示例,我得到了完全相同的答案。所以,sum_of_digits_squared(-123)等于sum_of_digits_squared(123)(这又等于14)(即使没有我显然应该添加的细节)。事实上,如果我尝试在屏幕上打印数字的数字(从重要性到最小),在123我得到3 2 1的-123情况下和在我得到的情况下-3 -2 -1(这实际上很有趣)。但在这个问题中,这无关紧要,因为我们对数字进行平方。
So, who's wrong?
那么,谁错了?
EDIT: My bad, I forgot to specify and didn't know it was important. The version of C used in our class and tests has to be C99 or newer. So I guess (by reading the comments) that my version would get the correct answer in any way.
编辑:我的错,我忘记指定并且不知道这很重要。我们的课程和测试中使用的 C 版本必须是 C99 或更高版本。所以我猜(通过阅读评论)我的版本会以任何方式得到正确的答案。
回答by Steve Summit
Summarizing a discussion that's been percolating in the comments:
总结在评论中渗透的讨论:
- There is no good reason to test in advance for
n == 0. Thewhile(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 ofa % bwas alwaysin the range[0 .. b-1], meaning that -123 % 10 was 7. On such a system, the test in advance forn < 0would be necessary.
- 没有充分的理由提前测试
n == 0. 该while(n)测试将完美地处理这种情况。 - 很可能您的老师仍然习惯于较早的时间,当时
%负操作数的结果定义不同。在某些老的系统(包括,特别是,早期的Unix上的PDP-11,其中丹尼斯里奇最初开发C),结果a % b是总的范围内[0 .. b-1],这意味着-123%10为7。在这样的系统中,测试提前为n < 0将是必要的。
But the second bullet applies only to earlier times. In the current versions of both the C and C++ standards, integer division is defined to truncate towards 0, so it turns out that n % 10is guaranteed to give you the (possibly negative) last digit of neven when nis negative.
但第二个要点仅适用于较早的时期。在 C 和 C++ 标准的当前版本中,整数除法被定义为向 0 截断,因此事实证明,即使是负数,n % 10也可以保证为您提供(可能为负数)的最后一位数字。nn
So the answer to the question "What is the meaning of while(n)?"is "Exactly the same as while(n != 0)", and the answer to "Will this code work properly for negative as well as positive n?"is "Yes, under any modern, Standards-conforming compiler."The answer to the question "Then why did the instructor mark it down?"is probably that they're not aware of a significant language redefinition that happened to C in 1999 and to C++ in 2010 or so.
所以这个问题的答案“是什么意思while(n)?” 是“完全一样while(n != 0)”,而回答“愿意为负此代码正常工作,以及积极的n?” 是“是的,在任何现代的、符合标准的编译器下。” 问题的答案“那老师为什么要记下来?” 可能是他们没有意识到 1999 年发生在 C 和 2010 年左右的 C++ 中发生的重大语言重新定义。
回答by klutt
Your code is perfectly fine
你的代码非常好
You are absolutely correct and your teacher is wrong. There is absolutely no reason at all to add that extra complexity, since it does not affect the result at all. It even introduces a bug. (See below)
你是绝对正确的,你的老师是错的。完全没有理由增加额外的复杂性,因为它根本不会影响结果。它甚至引入了一个错误。(见下文)
First, the separate check if nis zero is obviously completely unnecessary and this is very easy to realize. To be honest, I actually question your teachers competence if he has objections about this. But I guess everybody can have a brain fart from time to time. However, I DO think that while(n)should be changed to while(n != 0)because it adds a little bit extra clarity without even costing an extra line. It's a minor thing though.
首先,单独检查是否n为零显然是完全没有必要的,这很容易实现。老实说,如果他对此有异议,我实际上是在质疑您的老师的能力。但我想每个人都会时不时地放个屁。但是,我确实认为while(n)应该将其更改为,while(n != 0)因为它增加了一点额外的清晰度,甚至无需花费额外的线路。不过也是小事。
The second one is a bit more understandable, but he is still wrong.
第二个稍微好理解一点,但他还是错了。
This is what the C11 standard 6.5.5.p6says:
这是C11 标准 6.5.5.p6所说的:
If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a; otherwise, the behavior of both a/b and a%b is undefined.
如果商 a/b 是可表示的,则表达式 (a/b)*b + a%b 应等于 a;否则, a/b 和 a%b 的行为都是未定义的。
The footnote says this:
脚注是这样说的:
This is often called "truncation toward zero".
这通常称为“向零截断”。
Truncation toward zero means that the absolute value for a/bis equal to the absolute value for (-a)/bfor all aand b, which in turn means that your code is perfectly fine.
向零截断意味着 for 的绝对值a/b等于(-a)/b所有aand的绝对值b,这反过来意味着您的代码非常好。
Modulo is easy math, but may be counterintuitive
模数是简单的数学,但可能违反直觉
However, your teacher does have a point that you should be careful, because the fact that you're squaring the result is actually crucial here. Calculating a%baccording to above definition is easy math, but it might go against your intuition. For multiplication and division, the result is positive if the operands have equal sign. But when it comes to modulo, the result has the same sign as the firstoperand. The second operand does not affect the sign at all. For instance, 7%3==1but (-7)%(-3)==(-1).
然而,你的老师确实有一点你应该小心,因为你对结果进行平方这一事实在这里实际上是至关重要的。a%b根据上述定义进行计算是简单的数学运算,但它可能与您的直觉相反。对于乘法和除法,如果操作数具有等号,则结果为正。但是当涉及到模时,结果与第一个操作数具有相同的符号。第二个操作数根本不影响符号。例如,7%3==1但是(-7)%(-3)==(-1)。
Here is a snippet demonstrating it:
这是一个演示它的片段:
$ cat > main.c
#include <stdio.h>
void f(int a, int b)
{
printf("a: %2d b: %2d a/b: %2d a\%b: %2d (a%b)^2: %2d (a/b)*b+a%b==a: %5s\n",
a, b ,a/b, a%b, (a%b)*(a%b), (a/b)*b+a%b == a ? "true" : "false");
}
int main(void)
{
int a=7, b=3;
f(a,b);
f(-a,b);
f(a,-b);
f(-a,-b);
}
$ gcc main.c -Wall -Wextra -pedantic -std=c99
$ ./a.out
a: 7 b: 3 a/b: 2 a%b: 1 (a%b)^2: 1 (a/b)*b+a%b==a: true
a: -7 b: 3 a/b: -2 a%b: -1 (a%b)^2: 1 (a/b)*b+a%b==a: true
a: 7 b: -3 a/b: -2 a%b: 1 (a%b)^2: 1 (a/b)*b+a%b==a: true
a: -7 b: -3 a/b: 2 a%b: -1 (a%b)^2: 1 (a/b)*b+a%b==a: true
So, ironically, your teacher proved his point by being wrong.
因此,具有讽刺意味的是,您的老师通过错误证明了他的观点。
Your teacher's code is flawed
你老师的代码有问题
Yes, it actually is. If the input is INT_MINAND the architecture is two's complement AND the bit pattern where the sign bit is 1 and all value bits are 0 is NOT a trap value (using two's complement without trap values is very common) then your teacher's code will yield undefined behavioron the line n = n * (-1). Your code is - if ever so slightly - betterthan his. And considering introducing a small bug by making the code unnecessary complex and gaining absolutely zero value, I'd say that your code is MUCH better.
是的,确实如此。如果输入是INT_MINAND 架构是二进制补码,并且符号位为 1 且所有值位均为 0 的位模式不是陷阱值(使用没有陷阱值的二进制补码非常常见),那么您的老师的代码将产生未定义的行为就行了n = n * (-1)。你的代码 - 如果稍微好一点 -比他的好。考虑到通过使代码变得不必要的复杂并获得绝对零值来引入一个小错误,我会说您的代码要好得多。
In other words, in compilations where INT_MIN = -32768 (even though the resulting function cannot receive an input that is < -32768 or > 32767), the validinput of -32768 causes undefined behavior, because the result of -(-32768i16) cannot be expressed as a 16-bit integer. (Actually, -32768 probably would not cause an incorrect result, because -(-32768i16) usually evaluates to -32768i16, and your program handles negative numbers correctly.) (SHRT_MIN could be -32768 or -32767, depending on the compiler.)
换句话说,在 INT_MIN = -32768 的编译中(即使结果函数无法接收 < -32768 或 > 32767 的输入),-32768的有效输入会导致未定义的行为,因为 -(-32768i16) 的结果不能表示为 16 位整数。(实际上,-32768 可能不会导致不正确的结果,因为 -(-32768i16) 通常计算为 -32768i16,并且您的程序正确处理负数。)(SHRT_MIN 可能是 -32768 或 -32767,具体取决于编译器。)
But your teacher explicitly stated that ncan be in the range [-10^7; 10^7]. A 16-bit integer is too small; you would have to use [at least] a 32-bit integer. Using intmight seem to make his code safe, except that intis not necessarily a 32-bit integer. If you compile for a 16-bit architecture, both of your code snippets are flawed. But your code is still much better because this scenario reintroduces the bug with INT_MINmentioned above with his version. To avoid this, you can write longinstead of int, which is a 32-bit integer on either architecture. A longis guaranteed to be able to hold any value in the range [-2147483647; 2147483647]. C11 Standard 5.2.4.2.1LONG_MINis often -2147483648but the maximum (yes, maximum, it's a negative number) allowed value for LONG_MINis 2147483647.
但是你的老师明确表示n可以在 [-10^7; 10^7]。16 位整数太小;你将不得不使用 [至少] 一个 32 位整数。使用int似乎可以使他的代码安全,但它int不一定是 32 位整数。如果您针对 16 位架构进行编译,那么您的两个代码片段都有缺陷。但是您的代码仍然要好得多,因为这种情况在INT_MIN他的版本中重新引入了上面提到的错误。为避免这种情况,您可以编写long代替int,它是任一架构上的 32 位整数。Along保证能够保存范围 [-2147483647; 2147483647]。C11 标准 5.2.4.2.1LONG_MIN通常是-2147483648但是允许的最大值(是的,最大值,它是一个负数)LONG_MIN是2147483647。
What changes would I make to your code?
我会对您的代码进行哪些更改?
Your code is fine as it is, so these are not really complaints. It's more like that if I really, really need to say anything about your code, there are some small things that could make it just a tiny bit clearer.
你的代码很好,所以这些并不是真正的抱怨。更像是如果我真的,真的需要对你的代码说些什么,有一些小东西可以让它更清晰一点。
- The names of the variables could be a little bit better, but it is a short function that is easy to understand, so it's not a big deal.
- You could change the condition from
nton!=0. Semantically, it's 100% equivalent, but it makes it a little bit clearer. - Move declaration of
c(which I renamed todigit) to inside the while loop since it's only used there. - Change argument type to
longto ensure it can handle the whole input set.
- 变量的名字可能会好一点,但它是一个简短的函数,很容易理解,所以没什么大不了的。
- 您可以将条件从 更改
n为n!=0。从语义上讲,它是 100% 等效的,但它使它更清晰一点。 - 将
c(我重命名为digit) 的声明移动到 while 循环内,因为它只在那里使用。 - 将参数类型更改为
long以确保它可以处理整个输入集。
int sum_of_digits_squared(long n)
{
long sum = 0;
while (n != 0) {
int digit = n % 10;
sum += (digit * digit);
n /= 10;
}
return sum;
}
Actually, this can be a little bit misleading because - as mentioned above - the variable digitcan get a negative value, but a digit is in itself never either positive or negative. There are a few ways around this, but this is REALLY nitpicking, and I would not care for such small details. Especially the separate function for last digit is taking it too far. Ironically, this is one of the things that your teachers code actually solves.
实际上,这可能有点误导,因为 - 如上所述 - 变量digit可以获得负值,但数字本身永远不会是正值或负值。有几种方法可以解决这个问题,但这真的很吹毛求疵,我不会关心这么小的细节。特别是最后一位数字的单独功能太过分了。具有讽刺意味的是,这是您的教师代码实际解决的问题之一。
- Change
sum += (digit * digit)tosum += ((n%10)*(n%10))and skip the variabledigitcompletely. - Change the sign of
digitif negative. But I would strongly advice against making the code more complex just to make a variable name make sense. That's a VERY strong code smell. - Create a separate function that extracts the last digit.
int last_digit(long n) { int digit=n%10; if (digit>=0) return digit; else return -digit; }This is useful if you want to use that function somewhere else. - Just name it
cas you originally do. That variable name does not give any useful information, but on the other hand, it's not misleading either.
- 更改
sum += (digit * digit)到sum += ((n%10)*(n%10))并跳过变量digit彻底。 - 更改
digitif的符号为负。但是我强烈建议不要仅仅为了使变量名有意义而使代码更复杂。这是一种非常强烈的代码气味。 - 创建一个单独的函数来提取最后一位数字。
int last_digit(long n) { int digit=n%10; if (digit>=0) return digit; else return -digit; }如果您想在其他地方使用该功能,这很有用。 - 只需
c像最初一样命名即可。该变量名称没有提供任何有用的信息,但另一方面,它也没有误导性。
But to be honest, at this point you should move on to more important work. :)
但说实话,此时你应该继续做更重要的工作。:)
回答by Lee Daniel Crocker
I don't completely like either your version or your teacher's. Your teacher's version does the extra tests that you correctly point out are unnecessary. C's mod operator is not a proper mathematical mod: a negative number mod 10 will produce a negative result (proper mathematical modulus is always non-negative). But since you're squaring it anyway, no difference.
我不完全喜欢你的版本或你老师的版本。你的老师的版本做了额外的测试,你正确指出是不必要的。C 的 mod 运算符不是正确的数学 mod:负数 mod 10 将产生负结果(正确的数学模数始终为非负)。但既然你在平方它,没有区别。
But this is far from obvious, so I would add to your code not the checks of your teacher, but a big comment that explains why it works. E.g.:
但这远不是显而易见的,所以我不会在你的代码中添加你老师的检查,而是一个解释它为什么起作用的大注释。例如:
/* NOTE: This works for negative values, because the modulus gets squared */
/* 注意:这适用于负值,因为模数被平方 */
回答by Chipster
NOTE:AS I was writing this answer, you did clarify that you are using C. The majority of my answer is about C++. However, since your title still has C++ and the question is still tagged C++, I have chosen to answer anyway in case this is still useful to other people, especially since most of the answers I've seen till now are mostly unsatisfactory.
注意:在我写这个答案时,您确实澄清了您使用的是 C。我的大部分答案都是关于 C++。但是,由于您的标题仍然使用 C++ 并且问题仍然标记为 C++,因此我选择了回答,以防这对其他人仍然有用,尤其是因为到目前为止我看到的大多数答案大多都不令人满意。
In modern C++(Note: I don't really know where C stands on this), your professor seems to be wrong on both counts.
在现代C++ 中(注意:我真的不知道 C 在这方面的立场),你的教授在这两个方面似乎都错了。
First is this part right here:
首先是这部分在这里:
if (n == 0) {
return 0;
}
In C++, this is basically the same thing as:
if (!n) {
return 0;
}
That means your while is equivalent to something like this:
这意味着你的 while 相当于这样的:
while(n != 0) {
// some implementation
}
That means since you are merely exiting in your if when the while wouldn't execute anyway, there really isn't a reason to put this if here, since what you are doing after the loop and in the if are equivalent anyway. Although I should say that is for some reason these were different, you'd need to have this if.
这意味着,由于您只是在 if 中退出,而 while 无论如何都不会执行,因此确实没有理由将 if 放在这里,因为无论如何您在循环之后和 if 中所做的都是等效的。虽然我应该说这是出于某种原因这些是不同的,但你需要有这个如果。
So really, this if statement isn't particularly useful unless I'm mistaken.
所以真的,除非我弄错了,否则这个 if 语句并不是特别有用。
The second part is where things get hairy:
第二部分是事情变得棘手的地方:
if (n < 0) {
n = n * (-1);
}
The heart of the issue is is what the output of the modulus of a negative number outputs.
问题的核心是负数模数的输出是什么。
In modern C++, this seems to be mostly well defined:
在现代 C++ 中,这似乎大多是明确定义的:
The binary / operator yields the quotient, and the binary % operator yields the remainder from the division of the first expression by the second. If the second operand of / or % is zero the behavior is undefined. For integral operands the / operator yields the algebraic quotient with any fractional part discarded; if the quotient a/b is representable in the type of the result, (a/b)*b + a%b is equal to a.
二元 / 运算符产生商,二元 % 运算符产生第一个表达式除以第二个表达式的余数。如果 / 或 % 的第二个操作数为零,则行为未定义。对于整数操作数, / 运算符产生代数商,其中任何小数部分都被丢弃;如果商 a/b 在结果的类型中是可表示的,则 (a/b)*b + a%b 等于 a。
And later:
然后:
If both operands are nonnegative then the remainder is nonnegative; if not, the sign of the remainder is implementation-defined.
如果两个操作数都为非负,则余数为非负;如果不是,余数的符号是实现定义的。
As the poster of the quoted answer correctly points out, the important part of this equation right here:
正如引用答案的发布者正确指出的那样,这个等式的重要部分就在这里:
(a/b)*b + a%b
(a/b)*b + a%b
Taking an example of your case, you'd get something like this:
以你的情况为例,你会得到这样的东西:
-13/ 10 = -1 (integer truncation)
-1 * 10 = -10
-13 - (-10) = -13 + 10 = -3
The only catch is that last line:
唯一的问题是最后一行:
If both operands are nonnegative then the remainder is nonnegative; if not, the sign of the remainder is implementation-defined.
如果两个操作数都为非负,则余数为非负;如果不是,余数的符号是实现定义的。
That means that in a case like this, only the signseems to be implementation-defined. That shouldn't be a problem in your case because, because you are squaring this value anyway.
这意味着在这种情况下,似乎只有符号是实现定义的。在您的情况下,这应该不是问题,因为无论如何您都在平方这个值。
That said, keep in mind that this doesn't necessarily apply to earlier versions of C++, or C99. If that is what your professor is using, that could be why.
也就是说,请记住,这不一定适用于 C++ 或 C99 的早期版本。如果那是您的教授正在使用的,那可能就是原因。
EDIT:Nope, I'm wrong. This seems to be the case for C99 or later as well:
C99 requires that when a/b is representable:
(a/b) * b + a%b shall equal a
C99 要求当 a/b 可表示时:
(a/b) * b + a%b 应等于 a
And another place:
而另一个地方:
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.
当整数除法且除法不精确时,如果两个操作数都是正数,则 / 运算符的结果是小于代数商的最大整数,而 % 运算符的结果是正数。如果任一操作数为负,则 / 运算符的结果是小于代数商的最大整数还是大于代数商的最小整数是实现定义的,如 % 运算符结果的符号。如果商 a/b 是可表示的,则表达式 (a/b)*b + a%b 应等于 a。
So, yeah. Even in C99, this doesn't seem to affect you. The equation is the same.
是的。即使在 C99 中,这似乎也不会影响您。等式是一样的。
回答by C.B.
As others have pointed out, the special treatment for n==0 is nonsense, since for every serious C programmer it is obvious that "while(n)" does the job.
正如其他人指出的那样,对 n==0 的特殊处理是无稽之谈,因为对于每个认真的 C 程序员来说,“while(n)”显然可以完成这项工作。
The behaviour for n<0 is not that obvious, that's why I would prefer to see those 2 lines of code:
n<0 的行为不是那么明显,这就是为什么我更愿意看到这两行代码:
if (n < 0)
n = -n;
or at least a comment:
或至少评论:
// don't worry, works for n < 0 as well
Honestly, at what time did you start considering that n might be negative? When writing the code or when reading your teacher's remarks?
老实说,你是什么时候开始考虑 n 可能是负数的?在编写代码或阅读老师的评论时?
回答by SlowLearner
This reminds me of an assignment that I failed
这让我想起了我失败的作业
Way back in the 90's. The lecturer had been sprouting on about loops and, long story short, our assignment was to write a function that would return the number of digits for any given integer > 0.
回到90年代。讲师一直在谈论循环,长话短说,我们的任务是编写一个函数,该函数将返回任何给定整数 > 0 的位数。
So, for example, the number of digits in 321would be 3.
因此,例如,中的位数321将是3。
Although the assignment simply said to write a function that returned the number of digits, the expectation was that we would use a loop that divides by 10 until... you get it, as covered by the lecture.
尽管作业只是说要编写一个返回位数的函数,但期望我们将使用一个除以 10 的循环,直到...你明白了,如讲座所述。
But using loops was not explicitly stated so I: took the log, stripped away the decimals, added 1and was subsequently lambasted in front of the whole class.
但是没有明确说明使用循环,所以我:took the log, stripped away the decimals, added 1并随后在全班同学面前遭到抨击。
Point is, the purpose of the assignment was to test our understanding of what we had learned during lectures. From the lecture I received I learned the computer teacher was a bit of a jerk (but perhaps a jerk with a plan?)
重点是,作业的目的是测试我们对讲座中所学知识的理解。从我收到的讲座中,我了解到计算机老师是个混蛋(但也许是个有计划的混蛋?)
In your situation:
在你的情况下:
write a function in C/C++ that returns the sum of the digits of the number squared
用 C/C++ 写一个函数,返回数字平方的数字之和
I would definitely have provided two answers:
我肯定会提供两个答案:
- the correct answer (squaring the number first), and
- the incorrect answer in keeping with the example, just to keep him happy ;-)
- 正确答案(先将数字平方),以及
- 与示例一致的错误答案,只是为了让他开心;-)
回答by Paul McCarthy
Generally in assignments not all the marks are given just because the code works. You also get marks for making a solution easy to read, efficient and elegant. These things are not always mutually exclusive.
通常在作业中,并不是所有的分数都是因为代码有效而给出的。您还可以获得使解决方案易于阅读、高效和优雅的分数。这些东西并不总是相互排斥的。
One I can't strees enough is "use meaningful variable names".
我不能强调的是“使用有意义的变量名”。
In your example it does not make much difference, but if you're working on a project with a milion lines of code readability becomes very important.
在您的示例中,它没有太大区别,但是如果您正在处理具有数百万行代码的项目,可读性就变得非常重要。
Another thing I tend to see with C code is people trying to look clever. Rather than using while(n != 0)I'll show everyone how clever I am by writing while(n)because it means the same thing. Well it does in the compiler you have but as you suggested you teacher's older version has not implemented it the same way.
我倾向于在 C 代码中看到的另一件事是人们试图看起来很聪明。而不是使用while(n != 0)我将通过编写while(n)向大家展示我是多么聪明,因为它意味着同样的事情。好吧,它在您拥有的编译器中确实如此,但是正如您所建议的,老师的旧版本并没有以相同的方式实现它。
A common example is referencing an index in an array while incrementing it at the same time ; Numbers[i++] = iPrime;
一个常见的例子是引用数组中的一个索引,同时增加它;数字[i++] = iPrime;
Now, the next programmer who works on the code has to know if i gets incremented before or after the assignment, just so someone could show off.
现在,下一个处理代码的程序员必须知道 i 在赋值之前还是之后递增,以便有人可以炫耀。
A megabyte of disk space is cheaper that a roll of toilet paper, go for clarity rather than trying to save space, your fellow programmers will be happier.
一兆字节的磁盘空间比一卷卫生纸便宜,为了清晰而不是试图节省空间,你的程序员伙伴会更快乐。
回答by chqrlie
The problem statement is confusing, but the numerical example clarifies the meaning of the sum of the digits of the number squared. Here is an improved version:
问题陈述令人困惑,但数值示例阐明了数字平方的数字之和的含义。这是一个改进的版本:
Write a function in the common subset of C and C++ that takes an integer
nin the range [-107, 107]and returns the sum of the squares of the digits of its representation in base 10. Example: ifnis123, your function should return14(12+ 22+ 32= 14).
在 C 和 C++ 的公共子集中编写一个函数,该函数采用[-10 7, 10 7]
n范围内的整数,并返回其以 10 为基数表示的数字的平方和。示例:如果是,则您的函数应该返回(1 2+ 2 2+ 3 2= 14)。n12314
The function that you wrote is fine except for 2 details:
你写的函数很好,除了两个细节:
- The argument should have type
longto accommodate for all values in the specified range as typelongis guaranteed by the C Standard to have at least 31 value bits, hence a range sufficient to represent all values in [-107, 107]. (Note that typeintis sufficient for the return type, whose maximum value is568.) - The behavior of
%for negative operands is non-intuitive and its specification varied between the C99 Standard and previous editions. You should document why your approach is valid even for negative inputs.
- 参数应具有
long适应指定范围内所有值的类型long,因为 C 标准保证类型至少具有 31 个值位,因此该范围足以表示[-10 7, 10 7] 中的所有值。(请注意,typeint对于返回类型就足够了,其最大值为568。) %for 负操作数的行为是不直观的,它的规范在 C99 标准和以前的版本之间有所不同。您应该记录为什么您的方法即使对于负面输入也是有效的。
Here is a modified version:
这是一个修改后的版本:
int sum_of_digits_squared(long n) {
int s = 0;
while (n != 0) {
/* Since integer division is defined to truncate toward 0 in C99 and C++98 and later,
the remainder of this division is positive for positive `n`
and negative for negative `n`, and its absolute value is the last digit
of the representation of `n` in base 10.
Squaring this value yields the expected result for both positive and negative `c`.
dividing `n` by 10 effectively drops the last digit in both cases.
The loop will not be entered for `n == 0`, producing the correct result `s = 0`.
*/
int c = n % 10;
s += c * c;
n /= 10;
}
return s;
}
The teacher's answer has multiple flaws:
老师的回答有很多缺陷:
- type
intmay have an insufficient range of values. - there is no need to special case the value
0. - negating negative values is unnecessary and may have undefined behavior for
n = INT_MIN.
- type
int的值范围可能不足。 - 没有必要对 value 进行特殊处理
0。 - 否定负值是不必要的,并且可能具有未定义的行为
n = INT_MIN。
Given the extra constraints in the problem statement (C99 and range of values for n), only the first flaw is an issue. The extra code still produces the correct answers.
鉴于问题陈述中的额外约束(C99 和 的值范围n),只有第一个缺陷是一个问题。额外的代码仍然会产生正确的答案。
You should get a good mark in this test, but the explanation is required in a written test to show your understanding of the issues for negative n, otherwise the teacher may assume that you were unaware and just got lucky. In an oral exam, you would have gotten a question and your answer would have nailed it.
你应该在这个考试中取得好成绩,但在笔试中需要解释以表明你对否定问题的理解n,否则老师可能会认为你没有意识到并且只是走运。在口语考试中,您会得到一个问题,而您的答案会成功。
回答by Peter Krassoi
I wouldn't argue about whether the original or the modern definition of '%' is better but anyone who writes two return statement into such a short function shouldn't teach C programming at all. Extra return is a goto statement and we don't use goto in C. Furthermore the code without the zero check would have the same result, extra return made it harder to read.
我不会争论 '%' 的原始定义还是现代定义更好,但是任何将两个 return 语句写入如此短的函数的人根本不应该教 C 编程。Extra return 是一个 goto 语句,我们在 C 中不使用 goto。此外,没有零检查的代码将具有相同的结果,额外的 return 使其更难阅读。

