我应该为计数类成员使用无符号整数吗?
回答
例如,假设一个类
TList <T> = class
private
FCount : Cardinal;
public
property Count : Cardinal read FCount;
end;
Run Code Online (Sandbox Code Playgroud)
这确实有道理,不是吗?列表中存储的项目数不能为负数,那么为什么不使用无符号整数类型呢?我认为总是使用最不通用的(最特殊的)类型是一个很好的原则.
现在,迭代列表如下所示:
for I := 0 to List.Count - 1 do
Writeln (List [I]);
Run Code Online (Sandbox Code Playgroud)
当列表中存储的项目数为零时,编译器会尝试进行评估
List.Count - 1
Run Code Online (Sandbox Code Playgroud)
这导致一个很好的整数溢出(确切地说是下溢).结合调试器没有显示发生异常的适当位置的事实,这对我来说很难找到.
让我补充一点,如果你关闭溢出检查,结果错误将更难跟踪,因为那时你经常会访问不属于你的内存 - 这会导致未定义的行为.
从现在开始,我将为所有计数成员使用普通整数来避免这种情况.
如果这完全是胡说八道,请指出给我:)
(我只花了一个小时跟踪代码中的整数溢出,所以我决定分享一下 - 当然大多数人都会知道,但也许我可以节省一些时间.)
怎么能一个增加两个long,因此如果结果溢出,然后它被钳范围在Java中值Long.MIN_VALUE.. Long.MAX_VALUE?
对于添加ints,可以执行long精度算术并将结果转换回int,例如:
int saturatedAdd(int x, int y) {
long sum = (long) x + (long) y;
long clampedSum = Math.max((long) Integer.MIN_VALUE,
Math.min(sum, (long) Integer.MAX_VALUE));
return (int) clampedSum;
}
Run Code Online (Sandbox Code Playgroud)
要么
import com.google.common.primitives.Ints;
int saturatedAdd(int x, int y) {
long sum = (long) x + (long) y;
return Ints.saturatedCast(sum);
}
Run Code Online (Sandbox Code Playgroud)
但是在long没有较大的原始类型可以保持中间(非夹紧)总和的情况下.
由于这是Java,我不能使用内联汇编(特别是SSE的饱和添加指令).
它可以使用BigInteger例如
static final BigInteger bigMin = BigInteger.valueOf(Long.MIN_VALUE);
static final BigInteger bigMax = BigInteger.valueOf(Long.MAX_VALUE);
long …Run Code Online (Sandbox Code Playgroud) java math signal-processing bit-manipulation integer-overflow
编辑公共健康警告 - 此问题包含有关未定义行为的错误假设.见接受的答案.
在阅读了最近的博客文章后,我一直在思考避免C和C++代码中所有标准未定义假设的实用性.这是一个用C++剪切的片段,用于做无符号的128位加法......
void c_UInt64_Pair::operator+= (const c_UInt64_Pair &p)
{
m_Low += p.m_Low;
m_High += p.m_High;
if (m_Low < p.m_Low) m_High++;
}
Run Code Online (Sandbox Code Playgroud)
这显然依赖于关于溢出行为的假设.显然,大多数机器都可以支持正确类型的二进制整数(尽管可能是从32位块或其他任何东西构建的),但优化者可能会在这里利用标准未定义的行为.也就是说,m_Low < p.m_Low条件可以通过的唯一方法是m_Low += p.m_Low溢出,这是未定义的行为,因此优化器可以合法地确定条件总是失败.在这种情况下,这个代码就被打破了.
问题是,因此......
如何在不依赖未定义行为的情况下编写上述合理有效的版本?
假设您有一个适当的64位二进制机器整数,但您有一个恶意编译器,它将始终以最坏的(或不可能的)方式解释您的未定义行为.此外,假设您没有一些特殊的内置,内在,库或其他任何为您执行此操作的内容.
EDIT 略微澄清 - 这不仅仅是关于检测溢出,而且还确保m_Low和m_High都以正确的模2 ^ 64结果结束,这也是标准未定义的.
最近,我们在一些旧代码中发现了奇怪的行为.这段代码已经工作了很长时间,但在某些平台(XBox 360,PowerPC)上打破了编译器优化最大化.通常,我怀疑未定义的行为.
代码看起来大致如下:
#include <stdint.h>
uint32_t sign_extend16(uint32_t val)
{
return (int32_t)(int16_t)val;
}
Run Code Online (Sandbox Code Playgroud)
它是模拟器的一部分,所以有问题的操作不应该太奇怪.通常情况下,我希望这只考虑较低的16位并将其符号扩展为32位.显然,这是它多年来的行为.在x86_64上,GCC给出了这个结果:
0000000000000000 <sign_extend16>:
0: 0f bf c7 movswl %di,%eax
3: c3 retq
Run Code Online (Sandbox Code Playgroud)
但是,根据我对标准的理解,如果无法用signed类型表示unsigned的值,则无法定义将unsigned转换为signed.
那么编译器是否可以假设无符号值必须在范围内[0, 32767],因为任何其他值都是未定义的?在这种情况下,一个演员int16_t和另一个演员int32_t将无能为力.在这种情况下,编译器将代码转换为简单的移动是否合法?
从应用程序开发团队的角度来看,处理整数溢出(如999999*999999(结果> Integer.MAX_VALUE))的常见做法是什么?
人们可以BigInt强制要求并禁止使用Integer,但这是一个好/坏的想法?
EDU>> intmin
ans =
-2147483648
EDU>> abs(intmin)
ans =
2147483647
Run Code Online (Sandbox Code Playgroud)
这怎么可能?必须存在某种溢出,或者这些函数的定义以奇怪的方式混合在一起.
如何在INT_MIN不溢出的情况下提取绝对值?请参阅此代码以了解该问题:
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
printf("INT_MAX: %d\n", INT_MAX);
printf("INT_MIN: %d\n", INT_MIN);
printf("abs(INT_MIN): %d\n", abs(INT_MIN));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
吐出以下内容
INT_MAX: 2147483647
INT_MIN: -2147483648
abs(INT_MIN): -2147483648
Run Code Online (Sandbox Code Playgroud)
我需要这个来检查一个int值是否大于零.
至于这个问题是重复的为什么最大负整数-2147483648的绝对值仍然是-2147483648?,我不同意,因为这是一个如何,而不是一个为什么问题.
这里的bcache源包含以下行:
schedule_delayed_work(&dc->writeback_rate_update,
dc->writeback_rate_update_seconds * HZ);
Run Code Online (Sandbox Code Playgroud)
writeback_rate_update_seconds被定义为unsigned int,在x86_64上似乎是32位,我不确定是什么类型HZ,但我相信该值为1000并假设它是32位或更少.
如果我设置writeback_rate_update_seconds为2147483647,实际传递给什么值schedule_delayed_work?第二个参数schedule_delayed_work似乎是a long,但这并不意味着操作数在乘法溢出之前被提升为long,是吗?
如:
let mut a : usize = 0xFF;
a += -1; // -1 may be from other variable, so there can't be a -= 1;
println!("{}", a);
Run Code Online (Sandbox Code Playgroud)
输出是:
let mut a : usize = 0xFF;
a += -1; // -1 may be from other variable, so there can't be a -= 1;
println!("{}", a);
Run Code Online (Sandbox Code Playgroud)
反正?
据我所知,在 C 编程语言(以及许多基于 C 的语言)中,当算术运算溢出 N 位整数时,此溢出会将结果缩短为 2 的模 N 次方,仅保留 LSB结果。
当这样的整数算术运算在 Rust 编程语言中溢出时会发生什么。