【问题标题】:-32768 not fitting into a 16 bit signed value-32768 不适合 16 位有符号值
【发布时间】:2014-10-15 05:50:39
【问题描述】:

我正在使用 PCLint v 9.00h

在我的代码中,我有以下内容(其中 S16 是有符号的 16 位):

S16 temperatureResult = -32768;

除非我的大脑停止工作,否则这是适合这种类型的最小值

但我收到“违反 MISRA 2004 所需规则 10.1,将整数隐式转换为更小的类型”的 lint 错误

如果我将值更改为 -32767,它可以正常工作。

我是否遗漏了一些明显的东西?

【问题讨论】:

  • 如果我错了,请纠正我,但是 c99 TC3 不是说它是实现定义的,它们可能提供 + 和 - 值 0?因此,如果是这种情况,您将少 1 个负十进制值。
  • 使用非标准类型的原因是什么?如果你的编译器是 C99,你应该有 int16_tINT16_MIN 可以开箱即用。
  • @JensGustedt 这是一个由 MISRA 要求主导的编码标准要求,已经有一段时间没有更新了。不知道编译器是不是C99
  • 那个 MISRA 规则很奇怪。 Gcc(可能还有许多其他编译器)可以警告常量表达式的非值保留转换。演员隐藏了可能的错误;如果值更改为 -32769,编译器警告可能会有所帮助。

标签: c


【解决方案1】:

它不一定“不适合”。很可能它适合。 (你真的检查了吗?)

  • 如果您的平台使用 32 位(或更大)int,则所有算术表达式都以 32 位 int 类型计算,并且警告只是告诉您正在将 int 值转换为更小的值类型。 PCLint 根本不去检查实际值是否适合目标类型。

    您也许可以通过显式类型转换来抑制此警告

    S16 temperatureResult = (S16) -32768;
    
  • 如果您的平台使用 16 位 int,那么这里可能会涉及稍微不同的问题。在 C 语言中,-32768 不是原子常数。 -32768 实际上是一个表达式,由一个应用于正常量 32768 的一元 - 运算符组成。 32768 是一个不适合 16 位类型的正常量,因此编译器使用更大的类型(可能是 32 位 long int)来表示 32768。因此,-32768 在更大类型的域中被评估,并且最终也作为更大类型的值。 PCLind 决定警告您隐式切换到更大的类型。 (详情请参阅(-2147483648> 0) returns true in C++?。)

    如果这就是这里发生的情况,那么为了避免警告,您可以使用显式类型转换

    S16 temperatureResult = (S16) -32768;
    

    或者,您可以将初始化表示为

    S16 temperatureResult = -32767 - 1;
    

    在后一种情况下,编译器应该能够计算 16 位 int 类型域内的常量表达式。

【讨论】:

  • 在 C 中,所有整型文字都是 int 或更大的类型,所有整型表达式都是 int 或更大的类型。没有任何东西被评估为简短。
  • @n.m.:确实如此。 (我的回答在这方面具有误导性。确实,如果使用较大的类型,它不会是int。我从答案中删除了对int 的显式引用。)但是,可能是平台实际使用16 位 int?该语言允许使用 16 位 int。在这种情况下,问题是使用了更大的类型来评估表达式。不幸的是,警告消息没有提到确切的类型。
  • +1 很好地描述了正在发生的事情。但是对于解决方案,任何体面的编译器都应该有这些常量的宏,INT_MIN 如果这只是int 类型,或者INT16_MIN
【解决方案2】:

标准只保证有符号短的 [-32767,32767] 范围。您的实现可能会像大多数实现一样对此进行一些扩展,但 PCLint 会检查是否符合标准。

【讨论】:

  • 这很有趣。请问标准的哪个部分有这个
  • 5.2.4.2 N1570 中的数值限制
  • 为什么这个值很奇怪?对于 n+1 位值,范围不应该是 [-2^n, 2^n - 1] 吗?
  • @LưuVĩnhPhúc,所有位为0但最高位为1的值可能是陷阱表示。
  • @LưuVĩnhPhúc 有超越二进制补码表示的生命。一个人的补码和星座+量级曾经很常见。
【解决方案3】:

首先:它不必按标准处理-32768:

5.2.4.2.1 整数类型的大小

[...]

——short int 类型对象的最小值

SHRT_MIN -32767 // -(215 - 1)

——short int 类型对象的最大值

SHRT_MAX +32767 // 215 - 1

(我正在寻找关于支持 -32768 的环境定义注释的部分)

知道了:

本段说明了为什么有时会支持多一个数字的原因:

6.2.6.2 整数类型

[...]

2 — 对于有符号整数类型,对象表示的位应分为三个 组:值位、填充位和符号位。不需要任何填充位; 应该只有一个符号位。作为值位的每个位应具有与 对应无符号类型的对象表示中的相同位(如果有 有符号类型中的 M 个值位和无符号类型中的 N 个值位,则 M

——符号位为0的对应值取反(符号和幅度);

——符号位的值为-(2N)(二进制补码);

——符号位的值为 -(2N - 1)(反码)。

哪些适用是实现定义的,符号位为 1 的值是否适用 并且所有值位为零(对于前两个),或者带有符号位和所有值位 1(对于一个 补),是陷阱表示或正常值。在标志和 幅度和一个的补码,如果这个表示是一个正常值,它被称为 负零。

(我引用的所有内容都是用 ISO/IEC 9899:TC3 编写的)

【讨论】:

    【解决方案4】:

    想到试试:

    S16 温度结果 = (S16) 0x8000; // 假设二进制补码整数

    显式转换是因为Rule 10.1

    "整数类型表达式的值不应隐含 如果...则转换为不同的基础类型"

    使其便携:

    S16 温度结果 = -32767 - 1;

    但无论如何,如果 MISRA 需要与补充计算机(如某些 Cray 超级计算机)兼容,那么有符号 16 位的保证范围仅为 [-32767 ... 32767],因此您无法实现您的目标努力去做。

    【讨论】:

    • 同样的错误和一个额外的错误(信息丢失(初始化)(16位到15位))
    • @Firedragon 如果 MISRA 要求与补充计算机兼容,则保证的 16 位签名范围仅为 [-32767 ... 32767]。
    • 感谢您的信息。这听起来像是当时我不知道的补充,所以今天学到了一些新东西
    • 这会导致实现定义的行为。无论它是否与 MISRA 一致,鉴于有更便携的版本,您可能应该避免这样做。
    • @MattMcNabb 同意。用 C 编写可移植代码非常困难。无论如何,如果代码必须移植到补充计算机上,那么答案是“不能做”。
    猜你喜欢
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-06
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    相关资源
    最近更新 更多