在双精度数组上使用 memset(…, 0, …) 是否合法?

发布于 2024-10-10 18:30:18 字数 264 浏览 9 评论 0原文

将双精度数组的内存归零(使用 memset(..., 0, ...))或包含双精度的结构是否合法?

这个问题意味着两个不同的事情:

  1. 从 C 标准的角度来看:这是未定义的行为吗? (我认为,在任何特定平台上,这都不能是未定义的行为,因为它仅取决于浮点数的内存表示形式 - 仅此而已。)

  2. 从实际角度来看: 在Intel平台上可以吗? (无论标准怎么说。)

Is it legal to zero the memory of an array of doubles (using memset(…, 0, …)) or struct containing doubles?

The question implies two different things:

  1. From the point of view of C standard: Is this undefined behavior of not? (On any particular platform, I presume, this cannot be undefined behavior, as it just depends on the in-memory representation of floating-point numbers—that’s all.)

  2. From practical point of view: Is it OK on Intel platform? (Regardless of what the standard is saying.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

星星的轨迹 2024-10-17 18:30:18

C99 标准附录 F 说:

本附件指定了对 IEC 60559 浮点标准的 C 语言支持。这
IEC 60559 浮点标准专门针对二进制浮点运算
微处理器系统,第二版(IEC 60559:1989),先前指定
IEC 559:1989IEEE 二进制浮点运算标准
(ANSI/IEEE 754−1985)
基数无关浮点的 IEEE 标准
算术 (ANSI/IEEE 854−1987)
概括了二进制标准以删除
依赖于基数和字长。 IEC 60559 一般指浮点
标准,如 IEC 60559 操作、IEC 60559 格式等。
定义__STDC_IEC_559__应符合本附件中的规范。在哪里
表明了 C 语言和 IEC 60559 之间的绑定,IEC 60559 指定的
除非另有说明,行为均以引用方式采用。

并且,紧接着:

C 浮点类型与 IEC 60559 格式相匹配,如下所示:

  • float 类型与 IEC 60559 单一格式匹配。
  • double 类型与 IEC 60559 double 格式匹配。

因此,由于 IEC 60559 基本上是 IEEE 754-1985,并且由于它指定 8 个零字节表示 0.0(如 @David Heffernan 所说),这意味着 如果您发现 __STDC_IEC_559__ 已定义,您可以使用 memset 安全地进行 0.0 初始化。

The C99 standard Annex F says:

This annex specifies C language support for the IEC 60559 floating-point standard. The
IEC 60559 floating-point standard is specifically Binary floating-point arithmetic for
microprocessor systems, second edition (IEC 60559:1989)
, previously designated
IEC 559:1989 and as IEEE Standard for Binary Floating-Point Arithmetic
(ANSI/IEEE 754−1985)
. IEEE Standard for Radix-Independent Floating-Point
Arithmetic (ANSI/IEEE 854−1987)
generalizes the binary standard to remove
dependencies on radix and word length. IEC 60559 generally refers to the floating-point
standard, as in IEC 60559 operation, IEC 60559 format, etc. An implementation that
defines __STDC_IEC_559__ shall conform to the specifications in this annex. Where
a binding between the C language and IEC 60559 is indicated, the IEC 60559-specified
behavior is adopted by reference, unless stated otherwise.

And, immediately after:

The C floating types match the IEC 60559 formats as follows:

  • The float type matches the IEC 60559 single format.
  • The double type matches the IEC 60559 double format.

Thus, since IEC 60559 is basically IEEE 754-1985, and since this specifies that 8 zero bytes mean 0.0 (as @David Heffernan said), it means that if you find __STDC_IEC_559__ defined, you can safely do a 0.0 initialization with memset.

浮世清欢 2024-10-17 18:30:18

如果您谈论的是 IEEE754,则该标准将双精度的 +0.0 定义为 8 个零字节。如果您知道自己受 IEEE754 浮点支持,那么这是明确定义的。

至于Intel,我想不出在Intel x86/x64上不使用IEEE754的编译器。

If you are talking about IEEE754 then the standard defines +0.0 to double precision as 8 zero bytes. If you know that you are backed by IEEE754 floating point then this is well-defined.

As for Intel, I can't think of a compiler that doesn't use IEEE754 on Intel x86/x64.

为你拒绝所有暧昧 2024-10-17 18:30:18

David Heffernan 对你问题的第(2)部分给出了很好的答案。对于第 (1) 部分:

C99标准不保证一般情况下浮点值的表示。 §6.2.6.1 说:

除本子条款中所述外,所有类型的表示形式均未指定。

...该子条款没有进一步提及浮点。

你说:

(在固定平台上,这个 UB 怎么可能……它只取决于浮动表示,仅此而已……)

确实 - “未定义行为”和“未指定 行为”和“实现定义 行为”:

  • 未定义 行为”意味着任何事情都可能发生(包括运行时崩溃);
  • 未指定行为”意味着编译器可以自由地以任何它喜欢的方式实现一些合理的东西,但不需要记录实现选择;
  • 实现定义的行为”意味着编译器可以自由地以任何它喜欢的方式实现一些合理的东西,并且应该记录该选择(例如,参见 此处了解最新版本 < 中记录的实现选择a href="http://gcc.gnu.org/" rel="nofollow noreferrer">GCC);

因此,由于浮点表示是未指定的行为,它可能会以一种未记录的方式因平台而异(其中“平台”在这里意味着“硬件和编译器的组合”而不仅仅是“硬件”) )。

(我不确定如果定义了 __STDC_IEC_559__ ,则保证表示 double 使得所有位零为 +0.0 有多有用正如 Matteo Italia 的回答所述,实际上是在实践中,例如, GCC 从未定义过这一点。 ,尽管在​​许多硬件平台上使用 IEEE 754 / IEC 60559。)

David Heffernan has given a good answer for part (2) of your question. For part (1):

The C99 standard makes no guarantees about the representation of floating-point values in the general case. §6.2.6.1 says:

The representations of all types are unspecified except as stated in this subclause.

...and that subclause makes no further mention of floating point.

You said:

(on a fixed platform, how can this UB ... it just depends of floating representation that's all ...)

Indeed - there a difference between "undefined behaviour", "unspecified behaviour" and "implementation-defined behaviour":

  • "undefined behaviour" means that anything could happen (including a runtime crash);
  • "unspecified behaviour" means that the compiler is free to implement something sensible in any way it likes, but there is no requirement for the implementation choice to be documented;
  • "implementation-defined behaviour" means that the compiler is free to implement something sensible in any way it likes, and is supposed to document that choice (for example, see here for the implementation choices documented by the most recent release of GCC);

and so, as floating point representation is unspecified behaviour, it can vary in an undocumented manner from platform to platform (where "platform" here means "the combination of hardware and compiler" rather than just "hardware").

(I'm not sure how useful the guarantee that a double is represented such that all-bits-zero is +0.0 if __STDC_IEC_559__ is defined, as described in Matteo Italia's answer, actually is in practice. For example, GCC never defines this, even though is uses IEEE 754 / IEC 60559 on many hardware platforms.)

梦晓ヶ微光ヅ倾城 2024-10-17 18:30:18

即使您不太可能遇到出现问题的机器,但如果您真的像问题标题中所指出的那样谈论数组,并且如果这些数组是在编译时已知长度(即不是 VLA),那么只需初始化它们可能会更方便:

double A[133] = { 0 };

应该始终有效。如果稍后您必须再次将这样的数组归零,并且您的编译器符合现代 C (C99),您可以

memcpy(A, (double const[133]){ 0 }, 133*sizeof(double));

在任何现代编译器上使用复合文字来完成此操作,这应该与 memset 一样高效>,但优点是不依赖于 double 的特定编码。

Even though it is unlikely that you encounter a machine where this has problems, you may also avoid this relatively easily if you are really talking of arrays as you indicate in the question title, and if these arrays are of known length at compile time (that is not VLA), then just initializing them is probably even more convenient:

double A[133] = { 0 };

should always work. If you'd have to zero such an array again, later, and your compiler is compliant to modern C (C99) you can do this with a compound literal

memcpy(A, (double const[133]){ 0 }, 133*sizeof(double));

on any modern compiler this should be as efficient as memset, but has the advantage of not relying on a particular encoding of double.

风蛊 2024-10-17 18:30:18

正如 Matteo Italia 所说,根据标准这是合法的,但我不会使用它。类似的东西

double *p = V, *last = V + N;  // N is count
while (p != last) *(p++) = 0;

至少快两倍。

As Matteo Italia says, that’s legal according to the standard, but I wouldn’t use it. Something like

double *p = V, *last = V + N;  // N is count
while (p != last) *(p++) = 0;

is at least twice faster.

人心善变 2024-10-17 18:30:18

使用memset是“合法的”。问题在于它是否产生一个 array[x] == 0.0 为 true 的位模式。虽然基本的 C 标准并不要求这是真的,但我有兴趣听到事实并非如此的例子!

看起来通过 memset 设置为零相当于在 IBM-AIX、HP-UX (PARISC)、HP-UX (IA-64)、Linux(我认为是 IA-64)上分配 0.0 。

这是一个简单的测试代码:

double dFloat1 = 0.0;
double dFloat2 = 111111.1111111;

memset(&dFloat2, 0, sizeof(dFloat2));

if (dFloat1 == dFloat2) {
    fprintf(stdout, "memset appears to be equivalent to = 0.0\n");
} else {
    fprintf(stdout, "memset is NOT equivalent to = 0.0\n");
}

It’s “legal” to use memset. The issue is whether it produces a bit pattern where array[x] == 0.0 is true. While the basic C standard doesn’t require that to be true, I’d be interested in hearing examples where it isn’t!

It appears that setting to zero via memset is equivalent to assigning 0.0 on IBM-AIX, HP-UX (PARISC), HP-UX (IA-64), Linux (IA-64, I think).

Here is a trivial test code:

double dFloat1 = 0.0;
double dFloat2 = 111111.1111111;

memset(&dFloat2, 0, sizeof(dFloat2));

if (dFloat1 == dFloat2) {
    fprintf(stdout, "memset appears to be equivalent to = 0.0\n");
} else {
    fprintf(stdout, "memset is NOT equivalent to = 0.0\n");
}
迷雾森÷林ヴ 2024-10-17 18:30:18

好吧,我认为归零是“合法的”(毕竟,它是将常规缓冲区归零),但我不知道标准是否允许您对结果逻辑值进行任何假设。我的猜测是 C 标准将其保留为未定义。

Well, I think the zeroing is "legal" (after all, it's zeroing a regular buffer), but I have no idea if the standard lets you assume anything about the resulting logical value. My guess would be that the C standard leaves it as undefined.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文